This section describes how to persist an SMC-generated
finite state machine and restore it at a later date. In
the following examples I persist the FSM to a flat file
but they can be modified to work with other storage
types. The focus is only capturing the FSM's current
state and state stack. There is no other data to
persist in the SMC-generated code.
The examples use the class AppClass
which
has an associated finite state machine stored in its
data member _fsm
.
Note: the following sample code requires the .sm
file be compiled with the -serial
option.
If you are not using push/pop transitions, then
the use the following code to persist the current
state:
int
AppClass::serialize(
const char *
filename)
const
{
int
fd;
int
stateId(_fsm.getState().getId());
int
retcode(-1);
fd =
open
(filename,
(O_WRONLY | O_CREAT | O_TRUNC),
(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
if (fd >= 0)
{
retcode =
write
(fd, &stateId,
sizeof
(
int
));
(
void
)
close
(fd);
fd = -1;
}
return
(retcode);
}
Deserializing is the mirror:
int
AppClass::deserialize(
const char *
filename)
const
{
int
fd;
int
stateId;
int
retcode(-1);
fd =
open
(filename, O_RDONLY);
if (fd >= 0)
{
retcode =
read
(fd, &stateId,
sizeof
(
int
));
if (retcode >= 0)
{
_fsm.setState(_fsm.valueOf(stateId));
}
(
void
)
close
(fd);
fd = -1;
}
return
(retcode);
}
If you are using push/pop transitions, then the
serialization will be require your to persist the state
stack in reverse order (bottom to top) followed by the
current state.
Warning! Reading in the state stack results
in emptying the stack and corrupting the FSM. This
should not be a problem because you are persisting the
FSM for use later when you will restore the state
stack.
int
AppClass::serialize(
const char *
filename)
const
{
int
fd;
int
retcode(-1);
fd =
open
(filename,
(O_WRONLY | O_CREAT | O_TRUNC),
(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
if (fd >= 0)
{
int
size(_fsm.getStateStackDepth() + 1);
int
bufferSize(size *
sizeof
(
int
));
int
buffer[size + 1];
int
i;
buffer[0] = size;
buffer[size] = (_fsm.getState()).getId();
for (i = (size - 1); i > 0; --i)
{
_fsm.popState();
buffer[i] = (_fsm.getState()).getId();
}
retcode =
write
(fd, buffer, (bufferSize +
sizeof
(
int
)));
(
void
)
close
(fd);
fd = -1;
}
return
(retcode);
}
When reading in the persisted FSM, first read the
state count and then read in the states:
int
AppClass::deserialize(
const
char *
filename)
{
int
fd;
int
size;
int
retcode(-1);
_fsm.clearState();
fd =
open
(filename, O_RDONLY);
if (fd >= 0 &&
read
(fd, &size,
sizeof
(
int
)) >= 0)
{
int
bufferSize(size *
sizeof
(
int
));
int
buffer[size];
if (
read
(fd, buffer, bufferSize) >= 0)
{
int
i;
for (i = 0; i < size; ++i)
{
_fsm.pushState(_fsm.valueOf(buffer[i]));
}
}
}
if (fd >= 0)
{
(
void
)
close
(fd);
fd = -1;
}
return
(retcode);
}
SMC makes full use of Java's object serialization.
Assuming that AppClass declares
implements java.io.Serializable
and that
the _fsm
member data is not marked as
transient
, then by serializing the
AppClass instance will also serialize the FSM.
SMC considers the code it generates to be subservient to
the application code. For this reason the SMC code does
not serialize its references to the FSM context owner
or property listeners. The application code after
deserializing the FSM must call the
setOwner
method to re-establish the
application/FSM link. If the application listens for
FSM state transitions addStateChangeListener
must also be called to put the listeners in place.
public final class
AppClass
implements
java.io.Serializable
{
private
AppClassContext
_fsm;
public
AppClass()
{
_fsm =
new
AppClassContext
();
}
private
void
readObject(
java.io.ObjectInputStream
istream)
throws
java.io.IOException
,
ClassNotFoundException
{
istream.defaultReadObject();
_fsm.setOwner(
this
);
_fsm.addStateChangeListener(_stateListener);
return
;
}
public static
void
main(
String[]
args)
{
Serialize();
Deserialize();
}
public static
void
Serialize()
{
AppClass
appInstance =
new
AppClass
();
ObjectOutputStream
ostream =
new
ObjectOutputStream
(
new
FileOutputStream
(
"./fsm_serial.bin"
));
try
{
ostream.writeObject(appInstance);
}
catch
(
java.io.IOException
ioex)
{
}
finally
{
ostream.close();
}
}
...
}
Recreating the persisted AppClass instance is equally
simple:
public static
void
Deserialize()
{
AppClass
appInstance =
null
;
ObjectInputStream
istream =
new
ObjectInputStream
(
new
FileInputStream
(
"./fsm_serial.bin"
));
try
{
appInstance = (
AppClass
) istream.readObject();
}
catch
(
java.io.IOException
ioex)
{
}
finally
{
istream.close();
}
}
Note: the following sample code requires the .sm
file is compiled with the -serial
option.
Tcl persistance is similar to C++.
If you are not using push/pop transitions, then
the use the following code to persist the current
state:
public method
serialize {fileName} {
if
[
catch
{
open
$fileName w 0644} fileId] {
set
retcode
error
;
set
retval
"Failed to open ${filename} for writing"
;
}
else
{
puts
$fileId [[$_fsm getState] getId];
close
$fileId;
set
retcode
ok
;
set
retval
""
;
}
return
-code ${retcode} ${retval};
}
The following deserializes the current state:
public method
deserialize {fileName} {
if
[
catch
{
open
$fileName r} fileId] {
set
retcode
error
;
set
retval
"Failed to open ${filename} for reading"
;
}
else
{
gets
$fileId stateId;
$_fsm setState [$_fsm valueOf $stateId];
close
$fileId;
set
retcode
ok
;
set
retval
""
;
}
return
-code ${retcode} ${retval};
}
If you are using push/pop transitions, then the
serialization will be require your to persist the state
stack in reverse order (bottom to top) followed by the
current state.
Warning! Reading in the state stack results
in emptying the stack and corrupting the FSM. This
should not be a problem because you are persisting the
FSM for use later when you will restore the state
stack.
public method
serialize {fileName} {
if
[
catch
{
open
$fileName w 0644} fileId] {
set
retcode
error
;
set
retval
"${fileName} open failed"
;
}
else
{
set
state [$_fsm getState];
set
states {};
lappend
states [$state getId];
while
{[
catch
{$_fsm popState} retcode] == 0} {
set
state [$_fsm getState];
set
states [
linsert
$states 0 [$state getId]];
}
set
size [
llength
$states];
puts
$fileId $size;
foreach
stateId $states {
puts
$fileId $stateId;
}
close
$fileId;
set
retcode ok;
set
retval
""
;
}
return
-code ${retcode} ${retval};
}
When reading in the persisted FSM, first read the
state count and then read in the states:
public method
deserialize {fileName} {
if
[
catch
{
open
$fileName r} fileId] {
set
retcode
error
;
set
retval
"${fileName} open failed"
;
}
else
{
$_fsm clearState;
gets
$fileId size;
for
{
set
i 0} {$i < $size} {
incr
i} {
gets
$fileId stateId;
set
state [$_fsm valueOf $stateId];
$_fsm pushState $state;
}
close
$fileId;
set
retcode
ok
;
set
retval
""
;
}
return
-code ${retcode} ${retval};
}
VB.net
SMC makes full use of .net's object serialization.
Assuming that AppClass has the
<Serializable()>
attribute and that
the _fsm
member data is not marked as
<NonSerializable()>
, then
serializing the AppClass instance will also serialize
the FSM:
SMC considers the code it generates to be subservient to
the application code. For this reason the SMC code does
not serialize its references to the FSM context owner
or property listeners. The application code after
deserializing the FSM must call the
Owner
property setter to re-establish the
application/FSM link. If the application listens for
FSM state transitions, then event handlers must also
be put back in place.
Imports
System
Imports
System.IO
Imports
System.Runtime.Serialization
Imports
System.Runtime.Serialization.Formatters.Binary
<Serializable()>
Public Class
AppClass
Implements
IDeserializationCallback
Private
_fsm
As
AppClassContext
Public Sub New
()
_fsm =
New
AppClassContext(
Me
)
End Sub
Private Sub
OnDeserialization(
ByVal
send
As
Object
) _
Implements
IDeserializationCallback.OnDeserialization
_fsm.Owner =
Me
AddHandler _fsm.StateChange, handler
End Sub
Shared Sub
Main()
Serialize()
Deserialize()
End Sub
Shared Sub
Serialize()
Dim
appInstance
As New
AppClass()
Dim
stream
As
Stream
= _
File
.Open(
"fsm_serial.bin"
,
FileMode.Create
)
Dim
formatter
As New
BinaryFormatter
()
Try
formatter.Serialize(stream, appInstance)
Catch
serialex
As
SerializationException
Finally
stream.Close()
End Try
End Sub
...
End Class
Recreating the persisted AppClass instance is equally
simple:
Shared Sub
Deserialize()
Dim
appInstance
As
AppClass =
Nothing
Dim
stream
As
Stream
= _
File
.Open(
"fsm_serial.bin"
,
FileMode.Open
)
Dim
formatter
As New
BinaryFormatter()
Try
appInstance = _
CType
(formatter.Deserialize(stream), AppClass)
Catch
serialex
As
SerializationException
Finally
stream.Close()
End Try
End Sub
C#
SMC makes full use of .net's object serialization.
Assuming that AppClass has the
[Serializable]
attribute and that
the _fsm
member data is not marked as
[NonSerialized]
, then
serializing the AppClass instance will also serialize
the FSM:
SMC considers the code it generates to be subservient to
the application code. For this reason the SMC code does
not serialize its references to the FSM context owner
or property listeners. The application code after
deserializing the FSM must call the
Owner
property setter to re-establish the
application/FSM link. If the application listens for
FSM state transitions, then event handlers must also
be put back in place.
using
System
;
using
System.IO
;
using
System.Runtime.Serialization
;
using
System.Runtime.Serialization.Formatters.Binary
;
[Serializable]
public class
AppClass :
IDeserializationCallback
{
private
AppClassContext _fsm;
public
AppClass()
{
_fsm =
new
AppClassContext(
this
);
}
void
IDeserializationCallback.OnDeserialization(
Object
sender)
{
_fsm.Owner =
this
;
_fsm.StateChange += handler;
}
static
void
Main(
string
[] args)
{
Serialize();
Deserialize();
}
static
void
Serialize()
{
AppClass appInstance =
new
AppClass();
FileStream
fstream =
new
FileStream
(
"fsm_serial.dat"
, FileMode.Create);
BinaryFormatter
formatter =
new
BinaryFormatter
();
try
{
formatter.Serialize(fstream, appInstance);
}
catch
(
SerializationException
serialex)
{
}
finally
{
fstream.Close();
}
}
...
}
Recreating the persisted AppClass instance is equally
simple:
static
void
Deserialize()
{
AppClass appInstance =
null
;
FileStream
fstream =
new
FileStream
(
"fsm_serial.dat"
, FileMode.Open);
BinaryFormatter
formatter =
new
BinaryFormatter
();
try
{
appInstance = (AppClass) formatter.Deserialize(fstream);
}
catch
(
SerializationException
serialex)
{
}
finally
{
fstream.Close();
}
}