- Background
- Why FSM
- Intended Audience
- What's in the package
- Two Key Takeaways
- Main objective
- Concepts and Formats
- Usage overview
- Inverting a FSM
- Software Pattern
- Build the tool
- Examples of FSM
- Usage
- Code generation for a FSM
- Target platforms and tools
- Using examples
- How to continue
Alice and Bob are related. Bob is Alice's niece. One of them, Alice is retired, rich and childless. She appreciates, well expect, phone calls from her younger siblings. She wants to distribute inheritance plots according to active social competence.
Alice is generally unsure as older can be. Especially in connection with telephone calls. Is it really a nephew calling or a jerk? But she has established a safe way to talk, chat, over the phone. Through a formalized speech act.
She has build a Finite State Machine with state, inmessage and outmessage. By putting her finger in the current state, she knows what to expect from a credible nephew and what she can choose to answer at any moment. Then she changes state. Safe and secure for an old lady.
At one point she described this FSM to Bob over the phone; one transition at a time. Bob listened dutifully but only realized later that he could act like Alice himself. A state machine that helped him respond correctly to Alice's claims.
And then he came to the realization that he could twist or mirror her FSM to get his own correct FSM so that they could form two collaborative FSM. He went back to his recorded phone calls and start working.
This document is a description of how Bob went about it
- Fig 1 - Overview -
FSM (Finite State Machine) is a strong pattern when designing soft- and hardware. There are a plethora of aspects, theories and practices around this and with it a variety of values.
For example a lot of communication protocols are outlined using FSM. In my opinion the usage is poor and unprofessional.
Introduction to FSM can be found in
en.wikipedia.org/wiki/Finite-state_machine
An variant of FSM is the SDL which is covered in
https://en.wikipedia.org/wiki/Specification_and_Description_Language
Many protocols describing a client/server relation as one - and only one - helicopter FSM. To be useful there should be one FSM for each partner.
I think it would be more fruitful to describe the behavior for one part, the server, in a more rigourous FSM. Behavior is here a combination of inmessages, outmesages, conditions and states.
The other part, the client, can then be generated to a counterpart FSM by the algoritm presented herein.
Such a client could be used just to verify the server, or the client could be wrapped up as an API to a remote server or be a true client application. Or set up the sever as a test for an API implementation!
The mechanism to construct a counterpart FSM has no name because it is a new function. You may call it inverting, mirroring, complementing, counter-parting, mate, part...
- Communication Protocol designers and implementers
- Rust programmers
- C programmers
- Dialogue modeller
- Business Process modeller
- ...
- Basic concepts
- Inverting a FSM
- The Pattern FSMClass + GuardFunctions + OutMessageProducer + BusinessObject
- Different Views of a FSM
- The Tool. Build it here
- Generating your own FSM (Rust or C)
I think this material seems very chaotic with a lot of figures, unclear writings, poor language and no pedagogical presentation. But in any case try to understand two important and hopefully fruitful things.
-
A Finite State Machine with transitions of kind
StateNow,Inmessage,OutMessage,NextState
can be transformed, inverted into a counterpart FSM so, together, they constitute a kind of client and server. Out of one specification! That's what this tool is about! (The tool is though useful towards just a FSM without inverting).
More on inverting here -
The FSM is used to maintain the attributes for a Business Object of some kind. Not primarily maintaining the state! Business Object could be a Mac-address and lease time in DHCP. Or minimum speed for a Cruise Control in a car. Or order information in a sales order. When software engineering the FSM into the pattern the FSM itself is in fact stateless!
More on pattern here- FSM State and the Business Object are owned by the Invoker of the FSM
- The FSM Box is actually built on four classes/objects. A state/Event dispatch FSMClass, GuardFunctions, OutMessage Producer and a MessageFactory. As they are stateless they could have been built as plain functions!
- At each transition the Invoker feeds, lends out, the Box with InMessage, mutable State and mutable Business Object and gets an OutMessage back. Done.
- Short introduction
- Introduce some new formalism around FSM
- Offer a tool showing different views of a FSM
- Describe the inverting algorithm here
- Emulate a chat between a server and a client here
- Tool build
- Tool usage here
- Code generation for Rust and C here
A FSM is traditionally defined by a set of Transitions. Each transition contains
FromState, Event, NextState
In order to reduce the number of messages and also to be able to take care of bad messages a Guard function is applied on a state/event value. (The guard
is for historical reason from UML but perhaps selector, alternative, variant, conditions or similar would be better.) Each transition then looks like
FromState, Event, Guard Condition_1, NextState_1
FromState, Event, Guard Condition_2, NextState_2
As we are deeply intrested in the inverting opportunity we must be able to specify the output too. We got, after renaming event to inmessage the columns
FromState, InMessage, GuardCondition, OutMessage, NextState
A very simple FSM machine in source format - accepting Open,More...,Close
- might look like
INIT,Open,Guard_ONE,Out1,Process
Process,More,Guard_TWO,Out2,Process
Process,Close,Guard_THREE,Out3,FINAL
Note the mandatory INIT and FINAL state.
The tool could be used to display the FSM more tasty.
here > thetool testdata/server1.txt show --astty
+--State--+-InMessage-+-UserGuardValue-+-OutMessage-+-NextState-+
|INIT |Open |Cond_OK |OK |Process |
|Process |Close |Cond_OK |Ok |FINAL |
| |More |Cond_OK |OK |Process |
+---------+-----------+----------------+------------+-----------+
And the inverted
FSM becoms:
here > thetool testdata/server1.txt show --astty -i
+-----State-----+-InMessage-+---UserGuardValue---+-OutMessage-+---NextState---+
|INIT |Callin |INIT_Callin_4 |Open |INIT_Open |
|INIT_Open |OK |INIT_Open_OK_5 |Close |Process_Close |
| | |INIT_Open_OK_6 |More |Process_More |
|Process_Close |Ok |Process_Close_Ok_3 |nooutput |FINAL |
|Process_More |OK |Process_More_OK_1 |More |Process_More |
| | |Process_More_OK_2 |Close |Process_Close |
+---------------+-----------+--------------------+------------+---------------+
There are many advantages to representing a FSM in 1NF (First normal Form from Relational Database. Close to a CSV-file where all rows has the same column names). You can process data with traditional SQL but also with known patterns - filter, sort, select, group by, etc - in common programming languages.
- Fig 2 - Datamodell -
You can extend this database with whatever you need. Business Object, Source and Destination for Messages if you work with multiple FSM. You can add timers.
Or add a relation between OutMessages and guard-condition with an attribute like specification of which fields in the OutMessages should be produced depending on guard-condition. Look into the usage genspectext
Note. The transition is actually an SQL View. Quite easy to export as CSV.
The machines as input to the tool are defined as CSV-files with the implicit columns shown as
State,InMessage,UserGuardValue,OutMessage,NextState
Given a certain FSM source file you can:
- show
Different text displays of a FSM. astty, asmatrix, ascsv. Example - graph
More classic graphic views of a FSM. asbox, ascircle. Example - genspectext
Will generate an HTML page which can be dropped into a browser and copy pasted into a word processor. It holds sections for documenting relationships between In Message, Guard Functions, Out Message and Business Object. Example - chat
Will invoke a chat between a client and server. You will be involved as making the Guard Condition Choice. Try it - genc
- genrust
Will generate source for FSM, GuardFunctions and OutMessagProducer. For Rust or C
Note. You don't need to utilize or even care of the inverting facility. Just use the components for the server. The client and server are two separate compenents at build time and at run time.
Shortly. Join rows from the FSM transitions on itself and select distinct from the result where Next_State = State.
Out of that join do produce a transition row for the Inverter taking a smart combination of columns from left and right!
Add some transitions for the initial and final states in the Inverter. In the outlines below the final state is 'FINAL' and the initial state is 'INIT'
When joining two transaction (as Left and as Right) with
Left.NextState = Right.State
we can see
- The server will send
Left.OutMessage
and go toLeft.NextState
- The server will later accept
Right.InMessage
in thisRight.State
, which is the same asLeft.NextState
!
The invert to be generated must therefor have a transition looking like
state_N,Left.OutMessage,'-',Right.InMessage,state_M
How to assign namnes to these states?
We can look into the border construction for INIT
and FINAL
states
and use the same pattern for all.
INIT: ('callin' is used for start the inverted machine)
For all transitions with State = 'INIT'
add to the invert transitions
'INIT','callin',,event,'INIT'_event
FINAL: For all transitions with Next_State = 'FINAL' add to the invert transitions
'FINAL'_event,out,,'FINAL'
From that one can use the pattern (from SQL)
SELECT DISTINCT Concat(left.state, '_', left.inmessage) AS State,
left.outmessage AS Event,
'-' AS 'Guard Value',
right.inmessage AS Output,
Concat(right.state, '_', right.inmessage) AS 'NextState'
Here we have a working rouby source for inverting a FSM. You can copy and paste into a file and let ruby interpret it.
transitions = [
'INIT,Open,1,OpenOk,Process',
'Process,Add,1,AddOK,Process',
'Process,Add,2,RetryAdd,Process',
'Process,Add,3,AddFail,FINAL',
'Process,Close,1,CloseOk,FINAL',
'Process,Close,2,CloseFail,FINAL'
]
transitions.collect!{|row|row.split(',')}
mirror_trans =[]
transitions.each {|from,event,guard,out,nxt|
if from == 'INIT'
mirror_trans << ['INIT','callin','',event,'INIT'+'_'+event]
end
if nxt == 'FINAL'
mirror_trans << ['FINAL'+'_'+event,out,'','-','FINAL']
end
}
transitions.each {|l_from,l_event,l_guard,l_out,l_nxt|
transitions.each {|r_from,r_event,r_guard,r_out,r_nxt|
if l_nxt == r_from
mirror_trans << [l_from+'_'+l_event,l_out,'',
r_event,r_from+'_'+r_event]
end
}
}
mirror_trans.uniq!
guard_value = 0
mirror_trans.collect!{|from,event,guard,out,nxt|
guard_value += 1
[from,event,guard_value,out,nxt]}
mirror_trans.each{|from,event,guard,out,nxt|
puts from+','+event+','+guard.to_s+','+out+','+nxt}
Having FMS's in an SQL database is fruitful for many reasons. Versioning,checks, extensions etc
SELECT DISTINCT 'INIT' AS State,
'callin' AS Event,
'-' AS 'Guard Value',
inmessage AS Output,
Concat('INIT', '_', inmessage) AS 'NextState'
FROM transition
WHERE state = 'INIT'
AND fsm = 'adderserver'
UNION
(SELECT DISTINCT Concat(state, '_', inmessage) AS State,
outmessage AS Event,
'-' AS 'Guard Value',
'-' AS Output,
'FINAL' AS 'NextState'
FROM transition
WHERE nextstate = 'FINAL'
AND fsm = 'adderserver')
UNION
(SELECT DISTINCT Concat(first.state, '_', first.inmessage) AS State,
first.outmessage AS Event,
'-' AS 'Guard Value',
second.inmessage AS Output,
Concat(second.state, '_', second.inmessage) AS 'NextState'
FROM transition AS first
LEFT JOIN transition AS second
ON first.nextstate = second.state
WHERE first.fsm = 'adderserver'
AND second.fsm = 'adderserver')
The method for creating the Inverted FSM could be found in the source fms.rs and method is mirror_direct()
It's almost as compact as the ruby above.
Look into inverters/ and you will find the above source but also for Javascript, Smalltalk and Mathematica.
- Invoker, driver
- Business Object
- FSM State
- FSMClass + GuardFunctions + ProdOutMessages
- Message factory
All sources in the Box
below are generated with the tool for a certain FSM machine. Acutally every machine will be produced the same way with the same pattern but with different content. The invokers can be used on any machine.
- Fig 3 - Components -
Note. As there is a 1-1 exclusive mapping between state/inmessage in the FSM and the dedicated guardfunction this guardfunction could be inlined as source in the FSM. For performance reason. But it might be harder to maintain though.
On the distribution there is a Rust project which can be rebuilt directly.
> export THEDISTR=<...>/fsm2fsm
> #export THEDISTR=/Users/willy/MYRUST/fsm2fsm
> cd $THEDISTR/tool
> cargo clean
> cargo build
Ensure it was built
> alias thetool=$THEDISTR/tool/target/debug/tool
> thetool -h
You find them on tool/testdata
- dhcpserver.txt (proper)
- adderserver.txt (open,add,add_again,close)
- alice.txt (famous)
- alice1.txt (demo start)
- touchevents.txt (imported example on one-two finger move and scale)
- bufferserver.txt (zlib's deflate method)
- bgp1771server.txt (outline only)
- cruisecontroll.txt (embryo)
> thetool -h
Gives
<fsm> [-i] [options] show ( --astty | --ascsv | --asmatrix )
<fsm> [-i] [options] show --groupby --keys key1,.. --cols col1,...
<fsm> [-i] [options] genrust (--new | --update ) --destproject <directory>
<fsm> [-i] [options] genc (--new | --update ) --destproject <directory>
<fsm> [-i] [options] genspectext
<fsm> [-i] [options] calc
<fsm> [-i] [options] chat
<fsm> [-i] [options] graph [ --layoutnumber n ] ( --asbox | --ascircle )
<fsm> [-i] chat
<fsm> a finite state machine source file
--invert,-i Will work on first inverting the
--help,-h Show help
--astty Simple more pretty form of transactions
--ascsv Raw inputformat
--asmatrix Tabulated in event and state
--groupby Grouped by --keys, -k showing specified --cols, -c
--destproject Will generate source and drivers for a
Server and Client fsm talking to each other
--new Will generate all required sources
--update Will generate the sources for the FSM object only
--layoutnumber,-l Use a certain optional layoutnumber got from calc command
> thetool adderserver.txt show --astty
Gives a compact formatted text output.
( A banal server accepting a Open, zero or more Add and a final Close )
+--State--+-InMessage-+-UserGuardValue-+-OutMessage-+-NextState-+
|INIT |Open |1 |OpenOk |Process |
| | |2 |OpenFail |FINAL |
|Process |Add |1 |AddOK |Process |
| | |2 |RetryAdd |Process |
| | |3 |AddFail |FINAL |
| |Close |1 |CloseOk |FINAL |
| | |2 |CloseFail |FINAL |
+---------+-----------+----------------+------------+-----------+
The machine for a generated client (-i) looks like
> thetool adderserver.txt show --astty -i
+-----State-----+-InMessage-+------UserGuardValue-------+-OutMessage-+---NextState---+
|INIT |Callin |INIT_Callin_9 |Open |INIT_Open |
|INIT_Open |OpenFail |INIT_Open_OpenFail_11 |nooutput |FINAL |
| |OpenOk |INIT_Open_OpenOk_3 |Close |Process_Close |
| | |INIT_Open_OpenOk_8 |Add |Process_Add |
|Process_Add |AddFail |Process_Add_AddFail_7 |nooutput |FINAL |
| |AddOK |Process_Add_AddOK_10 |Add |Process_Add |
| | |Process_Add_AddOK_6 |Close |Process_Close |
| |RetryAdd |Process_Add_RetryAdd_4 |Close |Process_Close |
| | |Process_Add_RetryAdd_5 |Add |Process_Add |
|Process_Close |CloseFail |Process_Close_CloseFail_2 |nooutput |FINAL |
| |CloseOk |Process_Close_CloseOk_1 |nooutput |FINAL |
+---------------+-----------+---------------------------+------------+---------------+
Another text form is a kind of matrix with one row for each fromstate and one column for each tostate.
> thetool adderserver.txt show --asmatrix
+------------+----------+-----------+
|tostate -> | | |
| |-Process--|---FINAL---|
|fromstate -v| | |
+------------+----------+-----------+
|INIT |Open |Open |
| | 1 | 2 |
| | OpenOk | OpenFail |
+------------+----------+-----------+
|Process |Add |Add |
| | 1 | 3 |
| | AddOK | AddFail |
| |Add |Close |
| | 2 | 1 |
| | RetryAdd| CloseOk |
| | |Close |
| | | 2 |
| | | CloseFail|
+------------+----------+-----------+
This option let you view a FSM grouped on what columns you are interested in. If you like to see under what circumstances OutMessages are to be produced to type
> thetool dhcpserver.txt show --groupby --keys OutMessage --cols 0,1,2
+OutMessage++State------------+InMessage---------+UserGuardValue---+
|ACK ||WAITREQUEST |DECLINE |Sorry |
| ||WAITREQUEST |REQUEST |Enjoy |
| ||WAITREQUEST_REUSE|DECLINE |AcceptThat |
| ||WAITREQUEST_REUSE|RELEASE |OkReleased |
| ||WAITREQUEST_REUSE|REQUEST |OkReUse |
|INFORM ||IPAllocated |INFORM |OK |
|NAK ||INIT |DISCOVER |Error |
| ||INIT |DISCOVER_TRY_REUSE|OldNotIPAvailable|
|OFFER ||INIT |DISCOVER |NewIPAvailable |
| ||INIT |DISCOVER_TRY_REUSE|OldIPAvailable |
| ||IPAllocated |DISCOVER_TRY_REUSE|Free |
| ||IPAllocated |RELEASE |NowReleased |
|nooutput ||INIT |DISCOVER |NoIPAvailable |
| ||IPAllocated |tmo |Tmo2 |
| ||WAITREQUEST |tmo |Tmo1 |
| ||WAITREQUEST_REUSE|tmo |Tmo3 |
+----------++-----------------+------------------+-----------------+
Keys and columns could be either columnindex or named.
You can get a view of a FSM as a more traditional graph by
> thetool dhcpserver.txt graph --asbox >dhcpserver.svg
Drop the (standard) output onto any web browser and you get
- Fig 4 - DHCP server -
If there are too many crossig lines try optimise via
> thetool dhcpserver.txt calc
You get Optimum layout at number '1798345'
Use the iter value as value for --layoutnumber
> thetool dhcpserver.txt graph --layoutnumber 1798345 --asbox >dhcpserver.svg
In early phases when outlines of messages content is about to be drawn but also when a final public specification is to be produced a framework is needed. There is a possibility to get a basis for a structured text document.
Sections are
- Business Object
- In Messages
- Out Messages
- Guard functions
- Outmessage Production and Customer Object Updates
Run this function, drop the html file into a browser, copy and paste into your favorite word editor or import the html file direct if possible.
Could be a killer! Give it a try.
> thetool dhcpserver.txt genspectext >adderserver.html
Here is a partial screen scrape of that
- Fig 5 - Example genspectext -
This function will invoke a chat between a client and a server. You will be asked for making the guard condition decisions. Suitable for basic scenarios and use cases.
> thetool alice.txt chat
FSM Name=Client, State Now=INIT, In Event=Callin
Choose a Condition number
[
"0->INIT_Callin_19, Outevent=Hello_Alice, Nextstate=INIT_Hello_Alice",
"1->INIT_Callin_4, Outevent=Ping, Nextstate=INIT_Ping",
]
0
FSM Name=Server, State Now=INIT, In Event=Hello_Alice
Choose a Condition number
[
"0->Alert, Outevent=Hello_Bob, Nextstate=StartUp",
"1->Tired, Outevent=DontDisturb, Nextstate=Final",
]
0
FSM Name=Client, State Now=INIT_Hello_Alice, In Event=Hello_Bob
Choose a Condition number
[
"0->INIT_Hello_Alice_Hello_Bob_12, Outevent=How_Are_You, Nextstate=StartUp_How_Are_You",
]
0
FSM Name=Server, State Now=StartUp, In Event=How_Are_You
Choose a Condition number
[
"0->Good, Outevent=Rich, Nextstate=KeyExchage",
"1->Tired, Outevent=Tired, Nextstate=Final",
]
1
Server went into final state
The tool will generate a set of files for the actual FSM into a folder named generatedsources
. Note that the present make files assumes the folder name for the generated files is generatedsources
.
> thetool dhcpserver.txt genc [--what] (--new | --update) --destproject <projfolder>
For more information look at here
The tool will generate a set of files and folders for the actual FSM into the rustproject src
> thetool dhcpserver.txt genrust [--what] (--new | --update) --destproject <projfolder>
For more information look at here
The aim for code generation is to create sources for all (inner) components of a FSM. The outer parts, the invoker(s), are not generated but are the same for any machine you generate from. You can than try different scenarios for the same machine.
The distribution folder holds two folders:
tool
with sources for the toolexamples
with examples and source for C and Rust and scripts for build and execute FSM's.
The projects are ready made projects in Rust and C. The source is at examples
. There are 5 different kind of executables (invokers)
- A main holding a clientemulator and a serveremulator. Just ping-pong.
- A main reading stdin as In Message and driving a client FSM.
- A main reading stdin as In Message and driving a server FSM.
- A main server listening on UDP connections. The server is a multisession, single-threaded one. Each message is shorter than max UDP size.
- One or more clients connecting to the UDP server.
The stdin client or server could be useful for test/verification. Note that they are reading InMessages from a files called stdinCLIENT.txt and stdinSERVER.txt as the debuggers have problem supporting traditional stdin.
You can make a quick start by using VS Code and open a window on examples/rust/projects
or examples/c/projects/ANYfolder
. Build and debug existing Rust or C examples. Or look into the make files and Cargo.toml if you are not familiar with VS Code.
Source for both server and client are always generated.
The generated examples on examples has been build as follows
OS | Edit | Build | Debug | Run |
---|---|---|---|---|
macOS | VS Code | VS Code task (cargo build) | VS Code launch Plug-in | Terminal |
Windows | VS Code | VS Code (cargo build) | - | PowerShell |
Raspberry Pie | TextEdit | cargo build | - | shell |
OS | Edit | Build | Debug | Run |
---|---|---|---|---|
macOS | VS Code | VS Code task (make) | VS Code launch (cppdbg) | Terminal |
Windows | VS Code | VS Code task (cl) | VS Code launch (cppvsdbg) | PowerShell |
Raspberry Pie | TextEdit | make | - | shell |
- You can always look into the make* files (for C) to find out how to adjust your favorite IDE on Unix and also into the .vscode tasks and launch for Windows. (The makefiles does not have full dependency rules so adjust or always do a clean before build.)
- And of course look into the Cargo.toml and the hierarchy of mod.rs, *.rs and *-folder.
- On windows you must start the VS Code using 'Powershell for VS Code...'
Note. You can work on the
examples
immediately. It's generated with the alice FSM. You don't even need to copy them. Just build, debug and run.
Copy from examples
. So assign a path to the examples
folder via
> export FROMEXAMPLES=/Users/<...>/fsm2fsm/examples/
> # export FROMEXAMPLES=/Users/willy/MYRUST/fsm2fsm/examples/
We use the testdata/alice.txt
as an example for all targets.
When generating source files for machine note the following.
You can use the suboption --what
which will cause files to listed but not written.
The files will be written into the folder --destproject
x/generatedsources
> thetool testdata/alice.txt genc --what --new --destproject x
will give a list
File to be written= x/generatedsources/Messages.h
File to be written= x/generatedsources/CustomRealMsg.h
File to be written= x/generatedsources/MessageTypes.h
File to be written= x/generatedsources/C_GuardConditions.h
File to be written= x/generatedsources/S_GuardConditions.h
File to be written= x/generatedsources/C_GI.h
File to be written= x/generatedsources/S_GI.h
File to be written= x/generatedsources/C_GI.c
File to be written= x/generatedsources/S_GI.c
File to be written= x/generatedsources/C_PI.c
File to be written= x/generatedsources/S_PI.c
File to be written= x/generatedsources/C_PI.h
File to be written= x/generatedsources/S_PI.h
File to be written= x/generatedsources/C_FSM.h
File to be written= x/generatedsources/S_FSM.h
File to be written= x/generatedsources/C_FSM.c
File to be written= x/generatedsources/S_FSM.c
File to be written= x/generatedsources/MsgFactory.c
File to be written= x/generatedsources/MsgFactory.h
The files will be written into the folder --destproject
x/src
> thetool testdata/alice.txt genrust --what --new --destproject x
will give a list (note that several foldesr are used)
File to be written= x/src/shared_folder/realmessages.rs
File to be written= x/src/client_folder/messagesets.rs
File to be written= x/src/server_folder/messagesets.rs
File to be written= x/src/client_folder/msgfactoryclass.rs
File to be written= x/src/server_folder/msgfactoryclass.rs
File to be written= x/src/client_folder/fsmclass.rs
File to be written= x/src/server_folder/fsmclass.rs
File to be written= x/src/client_folder/guardclass.rs
File to be written= x/src/server_folder/guardclass.rs
File to be written= x/src/client_folder/prodmsgclass.rs
File to be written= x/src/server_folder/prodmsgclass.rs
File to be written= x/src/client_folder/guardconditions.rs
File to be written= x/src/server_folder/guardconditions.rs
File to be written= x/src/client_folder/prodconditions.rs
File to be written= x/src/server_folder/prodconditions.rs
When you update the set of transitions for a FSM you can regenerate some parts of the sources inside the FSM box. Use --update
- the FSMClass is regenerated
- the set of Messages and MessageTypes are regenerated
- the set of GuardConditions is regenerated
Other sources as below generated at --new
is not affected.
- C : CustomRealMsg, GI (Guard), PI (Prodmsg), MsgFactory.
- Rust : guardclass, prodmsgclass, msgfactoryclass, realmessages.
They are probably edited to conform to you real conditions and message content. Be ware that when rebuild you for sure get a lot of compiler errors. Depending on what changes you made to the machine
Elaborate on different changes in the machine like new name on states, message, new or deleted transitions, new tostate, fewer or more guardconditions.
I have tried to make all match-switch
in sources full domain without any defaults in order to ensure proper covering.
Here we go for C
> thetool testdata/alice.txt genc --what --update --destproject x
File to be written= x/generatedsources/Messages.h
File to be written= x/generatedsources/MessageTypes.h
File to be written= x/generatedsources/C_GuardConditions.h
File to be written= x/generatedsources/S_GuardConditions.h
File to be written= x/generatedsources/C_FSM.h
File to be written= x/generatedsources/S_FSM.h
File to be written= x/generatedsources/C_FSM.c
File to be written= x/generatedsources/S_FSM.c
and for Rust
> thetool testdata/alice.txt genrust --what --update --destproject x
File to be written= x/src/client_folder/messagesets.rs
File to be written= x/src/server_folder/messagesets.rs
File to be written= x/src/client_folder/fsmclass.rs
File to be written= x/src/server_folder/fsmclass.rs
File to be written= x/src/client_folder/guardconditions.rs
File to be written= x/src/server_folder/guardconditions.rs
File to be written= x/src/client_folder/prodconditions.rs
File to be written= x/src/server_folder/prodconditions.rs
Suppose you want a project in folder real MYPROJ
. Assign a path to that project and create the folder. First assign a dirpath to where you build the tool.
> export DESTPROJ=/Users/<...>/MYPROJ
> #export DESTPROJ=/Users/willy/MYRUST/MYPROJ
> mkdir $DESTPROJ
and copy all files from the distribution
> cp -R $FROMEXAMPLES/c/projects/* $DESTPROJ
It will look like
> ls -1p
clientserverfolder/
generatedsources/
stdinclientfolder/
stdinserverfolder/
udpclientfolder/
udpserverfolder/
If you are intrested just in one project like stdinserverfolder just do
> cp -R $FROMEXAMPLES/c/projects/generatedsources/* $DESTPROJ/generatedsources
> cp -R $FROMEXAMPLES/c/projects/stdinserverfolder/* $DESTPROJ/stdinserverfolder
When you generate source for a FSM the only folder affected is generatedsources
inside the DESTPROJ.
To generate the sources to the FSM box for testdata/alice.txt you do
> cd /Users/<...>/fsm2fsm/tool
> #cd /Users/willy/MYRUST/fsm2fsm/tool
> alias thetool=$THEDISTR/tool/target/debug/tool
> thetool testdata/alice.txt genc -d --what $DESTPROJ --new
> cd $DESTPROJ/clientserverfolder
> make -f makeclientserver clean
> make -f makeclientserver
Now execute the simple emulator ping pong for server and client
> ./clientandserver --seed 610
You get
seed=610
Client >>>>> Msg_ping >>> Server
Client <<<<< Msg_pong <<< Server
Client >>>>> Msg_how_are_you >>> Server
Client <<<<< Msg_rich <<< Server
Client >>>>> Msg_42 >>> Server
Client <<<<< Msg_43 <<< Server
Client >>>>> Msg_hm >>> Server
Client <<<<< Msg_or <<< Server
Client >>>>> Msg_no >>> Server
Client <<<<< Msg_what <<< Server
Client >>>>> Msg_other >>> Server
Client <<<<< Msg_bye <<< Server
Server is in finalstate
Client is in finalstate
Now generate and run the stdin programs. Note the generatedsources are the same in all examples.
> cd $DESTPROJ/stdinfolder
> make -f makestdinserver clean
> make -f makestdinserver
and run
./stdinserver --seed 610
You get
Server >>>>> Msg_hello_alice >>> Server
Server <<<<< Msg_hello_bob <<< Server
Server >>>>> Msg_how_are_you >>> Server
Server <<<<< Msg_tired <<< Server
The server went into final state. No more In Messages are allowed.
Here we build a server and a client which will interchange messages on UDP. The server is of kind multi-session but single-threaded.
> cd $DESTPROJ/udpfolder
> make -f makeudpserver clean
> make -f makeudpserver
> make -f makeudpclient clean
> make -f makeudpclient
Now start the server first and then one or more clients. One console window for the server and one for each client.
> ./udpserver --seed 610
> ./udpclient --seed 610
Client >>>>> Msg_ping >>> Server
Client <<<<< Msg_pong <<< Server
Client >>>>> Msg_how_are_you >>> Server
Client <<<<< Msg_tired <<< Server
Client is now in finalstate
In the server console we get
New session on (Port:27074)
Client (Port:27074) >>>>> Msg_ping >>> Server
Client (Port:27074) <<<<< Msg_pong <<< Server
Client (Port:27074) >>>>> Msg_how_are_you >>> Server
Client (Port:27074) <<<<< Msg_tired <<< Server
Session (Port:27074) went into finalstate
Suppose you want a project in folder real MYPROJ
. Assign a path to that project and create the folder. First assign a dirpath to where you build the tool.
> export DESTPROJ=/Users/<...>/MYPROJ
> #export DESTPROJ=/Users/willy/MYRUST/MYPROJ
> mkdir $DESTPROJ
and copy all files from the distribution
> cp -R $FROMEXAMPLES/rust/projects/* $DESTPROJ
It will look like. It holds one Cargo.toml for all builds. Browse that one in order to understand the file structure.
> ls -1p
Cargo.lock
Cargo.toml
src/
stdinCLIENT.txt
stdinSERVER.txt
target/
and the /src holds four *.rs file implementing each main()
>ls -1p src
client_folder/
clientandserver.rs
clientandserver_folder/
server_folder/
shared_folder/
stdinclient.rs
stdinserver.rs
udpclient.rs
udpserver.rs
When you generate source for a FSM the only folder affected are client_folder
and server_folder
inside the DESTPROJ.
To generate the sources to the FSM box for testdata/alice.txt you do
> cd /Users/<...>/fsm2fsm/tool
> #cd $THEDISTR
> alias thetool=$THEDISTR/tool/target/debug/tool
and gen for rust
> thetool tool/testdata/alice.txt genrust -d $DESTPROJ --new
> cd $DESTPROJ/clientserverfolder
> cargo clean
> cargo build
Now execute the simple emulator ping pong for server and client
> ./target/debug/clientandserver --seed 2
You get
Client >>>>>> Msg_ping,siiiiiie >>>>>>> Server
Client <<<<<< Msg_pong,shhhhhhs <<<<<<< Server
Client >>>>>> Msg_how_are_you,seeeeees >>>>>>> Server
Client <<<<<< Msg_tired,slllllls <<<<<<< Server
Server_main_Object:: fsm went into final state
Client_main_Object:: fsm went into final state
You can edit the file stdinSERVER.txt
which holds messages to be sent to the client. And try different --seed
values!
> ./target/debug/stdinserver --seed 0
You get
Stdin >>>>>> Msg_hello_alice,Nice you answered >>>>>>> FSM
Stdout <<<<<< Msg_hello_bob,sffffffN <<<<<<< FSM
Stdin >>>>>> Msg_how_are_you,mummel mummel >>>>>>> FSM
Stdout <<<<<< Msg_rich,sjjjjjjm <<<<<<< FSM
Stdin >>>>>> Msg_24,OKj o owijefowij efoj? >>>>>>> FSM
Stdout <<<<<< Msg_questionwas,siiiiiiO <<<<<<< FSM
Stdin >>>>>> Msg_24,OKj o owijefowij efoj? >>>>>>> FSM
Stdout <<<<<< Msg_questionwas,siiiiiiO <<<<<<< FSM
Stdin >>>>>> Msg_24,OKj o owijefowij efoj? >>>>>>> FSM
Stdout <<<<<< Msg_questionwas,siiiiiiO <<<<<<< FSM
Stdin >>>>>> Msg_24,OKj o owijefowij efoj? >>>>>>> FSM
Stdout <<<<<< Msg_questionwas,siiiiiiO <<<<<<< FSM
Stdin >>>>>> Msg_24,OKj o owijefowij efoj? >>>>>>> FSM
Stdout <<<<<< Msg_questionwas,siiiiiiO <<<<<<< FSM
Stdin >>>>>> Msg_42,OKj o owijefowij efoj? >>>>>>> FSM
Stdout <<<<<< Msg_43,saaaaaaO <<<<<<< FSM
Stdin >>>>>> Msg_well,iwellwellwellwellwellwell >>>>>>> FSM
Stdout <<<<<< Msg_bye_bob,sddddddi <<<<<<< FSM
stdinserver:: fsm went into final state
We have built a server and a client which will interchange messages on UDP. The server is of kind multi-session but single-threaded. If you have built the C version you can of cource intermix C and Rust udp executables.
Now first start the server and then one or more clients. One console window for the server and one for each client.
> ./target/debug/udpserver --seed 1232
> ./target/debug/udpclient --seed 610
Client >>>>> Msg_ping >>> Server
Client <<<<< Msg_pong <<< Server
Client >>>>> Msg_how_are_you >>> Server
Client <<<<< Msg_tired <<< Server
Client is now in finalstate
- Define a machine with transitions or use one existing.
- Generate the specifiction template
genspectext
- Use the project stdinsever as driver.
- Edit the first message in stdinSERVER.txt. Use some basic JSON as format.
- Edit
CustomRealmsg.h
and define a computational structure. - Edit
MsgFactory.c
(wire2comp_) - Edit S_BusinessObject.h to take care of inmessage.
- Edit S_GI.c the guardfunctions to further checks.
- Edit S_PI.c and take care of updates to BusinessObject.
- Creating an outmessage. Edit
CustomRealmsg.h
andMsgFactory.c
(wire2comp) - First turn is done!
- Continue with next inmessage
Chat the cruisecontroller.
When making dialogues, in MVC patterns, you can map controls to inmessages. The FSM will tell which controls should be enabled in each state. Use the FSM to creta runtime enabling of controllers. At higer level where you have a FSM managing several views you tell which views should be visibal or enabled at each state.
- Build an eventlogger for different devices which will collect events to a server managing one file for each device.
- Offer an API to the client with parameters like id=unitnam,data and dataleegth.
- The API is an invoker to the Client FSM Box. The business object is the id and the data to be collected in one or more UDP records.
- Both client and server must have time out on recvfrom. Use
select
with some time out on thefiledescriptors
for the sockets. Gently add a time-out message into the FSM components. - The server will collect each record and append that to the proper file.
- The server must accept a message from an operator to backup one logfile to other back ups in order to keep the logifile small.
- Start with FSM testdata/adderserver.txt and look into the udp* projects.
Timers are covered at all above. There are several requirements on this. Timeout, keepalive, ownership, mutual, absolute or delta, etc
Enhance the database with diffrent timers. Make skeletons and injections/generators.
There are many RFC using FSM in order to define behavior etc. In the case of client/server there is a mix of message content definitions and message content checks.
So, pick your favorite RFC, outline a basic FSM, or split into several FSM (several Business Objects?), and use the tool with --genspectext
to get a word document and try to copy portion from the RFC into your document.