I. 03f522 Project Ideas - RJL 031021 RJLRef: ~/03f522RefactoringSlides.doc.
[ For more details and 03f522
project ideas, see 03f522RefactoringProjects.htm
.]
State models are now on our
schedule. I require projects involving
State Models because modeling behavior is essential to achieve MDA goals:
·
One of these
projects is upgrading bde/src code.
·
Another is
upgrading genv12 to generate the many changes I made to
bde/pr_util/pr_*.c.
·
A third project
is to finish the bdeState project of
Paul Kingston, which did not have an alpha- or Linux-compatible pr_util library from chgen (genv12 was
developed concurrently with bdeState).
-----------------------------------------------------------------------------
·
Other projects are
applications of bde and/or LCP. They move the Hominid project toward
two-person game-playing by allowing two actors to take turns editing a bde
diagram (the gameboard) by moving, adding or deleting pieces (icons) at
appropriate cells. Possible examples are
tic-tac-toe, checkers and chess.
[Note that two bde processes can communicate by
connecting each one's logging output to
the partner's replay input. This requires SIMULTANEOUS logging and replay by
each participant, instead of the separate log and replay phases of the Hominid
project.]
·
Other projects
might involve the VC++ or Java version of bde (bde2vcpp or bde2java). These are
more or less data-compatible with bde, but they do NOT reuse code from bde, so
their maintenance has always been a problem.
[E.g., bde2java uses ASCII pfkeys internally; it does
not encode them to/from integers during import/export (equivalent to
pr_load/pr_dump). Bde2vcpp does not support
logging and replay. The more
recent genjava code generator supports logging and replay, so bde2java could be
re-factored to use it. The more recent gencpp produces C++ classes and STL-like
containers from an arbitrary schema, so COOL components BDE and LCP could be re-factored
to use gencpp. Then gencpp would replace chgen for bde use and bde2vcpp could
be re-factored or merged to achieve bde
code reuse.]
II. Analysis for refactoring of bdeReplay.cc and pr_log.c:
This week I have been trying
to resolve bugs in bde's use with genv12
-log with BDELOG, GENLOG and GENV12 #defined in configure.h for Imakefile use. This
requires a continuing analysis (reverse engineering) of bdeReplay and pr_log.c.
--------------------------------------------------------------------------
The replay_log function was
designed as a state machine in bdeReplay.cc. However, its two core functions
encode_token and do_command turned out to be independent of bde and Xwindows
and were soon moved to pr_log.c . Now I
believe the rest of replay_log can also
be moved to pr_log.c so the database
update process is entirely independent of the GUI (X11 or MSWin :-) and can be
completely auto-generated by genv12. (Right now, bde/pr_util/pr_log.c was
manually edited after genv12 generation, so more work like the project sjaganat
did between genv11 and genv12.)
[The source code refs below are all to $RL/pr_util and
$RL/src,
where $RL
is ~lechner/bde2alpha_rl/sandbox/bdecheckout/bde.
(Line #s are approximate because I am gradually
changing this code.)]
Function pr_replay (lines 1978-2094 of pr_log.c) begins the logfile replay process. It strips
off any trailing .typ suffix and appends "DB1.dat" to the logfile
name.
[The logfile to be replayed
was designed to have type .txt, and the checkpoint file (below) has type .dat,
but I want both file type extensions to become ".bde".]
Let's call the logfile
'anyfile.txt for now. In bde, pr_init
and pr_load are called first with anyfile.*
as argument. Pr_load does its
work in load_data (lines 2455-2554 of pr_load.c); load_data ignores the .txt or .dat suffix.
For ordinary .dat files
load_data calls pr_parse and pr_link (once per fkey) on every input record to load the database. However, iff it detects the "SR" command on line 1, load_data calls pr_log to handle the replay process.
Function pr_log does its work
by making the first call to function replay_log, currently in bdeReplay.cc
lines 147-438 . Replay_log's
state machine model has 3
states: STATE0, STATE1, STATE2.
These states are cases of
switch(replayState), and should have better, more meaningful enumerated names.)
STATE0 does initialization, STATE1
interprets each command line, and STATE2 does cleanup.
[Pr_log enters replay_log
only once, at its initial state; thereafter, STATE1 is entered from the
Keypress callback function. Keypress
(lines 440-501 of bdeReplay.cc) is registered as an Xwindows callback function
at bde/src/init.cc (line 220). Keypress
is small enough to be printed in its entirety: (Note Keypress is a no-op unless
#defined BDELOG.]
[For eight slides discussing
bdeReplay.cc and Keypress.cc in bde/src,
see KeypressSTD.htm ]
Keypress does nothing unless BDELOG is #defined at compile time
and the X11 event-type is
'KeyPress'. The pressed key (from the
X11 event structure) is restricted to be the letter 's'. If
this 'guard condition' on the event from X11 is satisfied:
(strcmp (XKeysymToString(keysym), "s") == 0)
&& (ReplayReady == 1)),
then Keypress calls replay_log() in pr_log.c to
interpret the command and return replayStatus.
[Think of 's' as
a SINGLE-STEP key for advancing the replay process one update transaction at a
time - other ways to make X11 process log file inputs are documented in Paul
Kingston's OOAD project.]
For more details of
replay_log see $CASE/03f522/bdeReplayuserGuideR1.031027
------------------------------------------------------------------
Compare this method to the
single-step operation of the BridgePoint demo URL, in which humans enter the
data and step thru the prototype. Instead, here bde logs the data or other data
generators like the Hominid project generate the log file. (Think of this
process as simulating the environment that generates messages to LCP state
machines.)
In other words, replay_log executes ALL commands BEFORE Keypress
enters its state model below. If
replay_log fails (replayStatus <
0), the switch() below is skipped over,
NOT entered. Any remaining actions
inside the case switch that are NOT bde-specific, but apply to EVERY
application that #defines GENLOG, should be moved into replay_log. This
includes the switch(state) if possible.
III. Source code of Keypress callback function (end of bdeReplay.cc):
void Keypress(Widget w,
XtPointer input, XEvent *event,
Boolean *continue_dispatch)
{
#ifdef BDELOG
extern int ReplayReady; //in init.cc; used in
dialog.cc, bdeReplay.cc
KeySym keysym;
Modifiers dummy;
int replayStatus; // local var? also in fileio.cc
if(event -> type == KeyPress)
{
XtTranslateKeycode(XtDisplay(w), event->xkey.keycode,
event-> xkey.state,
&dummy, &keysym);
dprintd("Replay Ready : %d\n",
ReplayReady);
if ( (strcmp (XKeysymToString(keysym),
"s") == 0)
&& (ReplayReady == 1) )
replayStatus
= replay_log();
if (replayStatus < 0) //0 if PASS, negative if FAIL
printf("Keypress: replay_log error,
token %d ignored\n", replayStatus);
else {
switch(replayState) { /*New - RJL 031019: State sequence: (0 1* 2) */
case STATE0: /* "SR" command */
replayState = STATE1;
case STATE1: /* while not EOF, read command at Keypress */
/* bde's ReDraw should be called here, after
replay_log - RJL 031018 */
if (command == StopCmd)
replayState = STATE2; // stop the iteration at EOF
break;
case STATE2 : /*
"SP" command or EOF */
ReplayReady = 0; //
ignore Keypress from now on
// or until a new "SR? (to concat. mult.
replay logs?)
dprint("bdeReplay:
Keypress: STATE2: End of Replay \n");
break;
default:
dprintd("bdeReplay.cc:Keypress:
Invalid STATE %d \n", replayState);
};
// end switch(replayState)
} //
end if replayStatus
} //
end if event->type==KeyPress
#endif /* ifdef BDELOG */
} // end
KeyPress
----------------------------------------------------------------
IV. Simplifiying the Keypress calback
(can we move its
state model to pr_log.c?)
To simplify Keypress, we must
find another place for any bde-specific actions done in the early part of
Keypress or inside its state model switch(replayState) which executes one of
the state action code blocks on each logfile command line.
STATE0: Keypress does nothing when it enters this first
state action case block. No bde-specific
functions are done in STATE0. STATE0 is
the value assigned to replayState by replay_log
as a post-condition of detecting the command "SR", and its
action is empty except to assign ReplayReady = 1. It also updates replayState
to STATE1; this 'state transition'
should be the last thing done in a state's
action code.
STATE1: The second action block is case
STATE1=1. No bde-specific functions are
done in STATE1. STATE1 becomes the value of replayState on all
subsequent Keypress calls except the
last one. ReplayReady==1 is a
pre-condition to doing anything when Keypress is called, and this is a result
of finding an "SR" command on the first non-comment line of the input
file.
The only state change in
Keypress is to assign replayState =
STATE2 iff (command ==StopCmd). The
database update responsibilties of
STATE1 have already been performed by calling do_command in replay_log.
One performance and human factors problem remains in State1: a brute-force approach is used to update the
canvas window by calling updatedisplaylist and ReDraw after each database
update transaction. This is slow and it flickers the display because the
interface level is too fine-grained.
ReDraw could be called less often if multiple updates were performed
before the screen is updated.
STATE2: This state is responsible for calling
updatedisplaylist after a database
update to convert the world coordinate data to device coordinates and then to
call ReDraw if the visible diagram changes.
Right now we use the brute-force technique of calling both of these no
matter what database update command is received. Only commands that do NOT affect the display
of the diagram are ignored.
V. Generic
(bde-independent) actions in pr_log.c:
This code contains the 'official' format of the
log file from genv12.
Function do_command (lines
1838-1975 in pr_log.c) interprets one command line (arg1) of the replay logfile. It calls encode_token( char* token, int
command). Both functions are at the end
of (a very long) pr_log.c.
The allowed commands are
enumerated in the encode_token function, which converts a string token into an
enumerated command. Command effects are
documented in do_command case blocks. The enumerated list of command values is defined at line 82 (approx) of
$RL/bde/pr_util/pr_log.c. Encode_token
is in lines 1803-1835 (approx) of pr_log.c.
Function do_command enters
one big switch(command) for the enumerated case names. (So does the
switch(command) inKeypress. Command tokens fall into two groups based on their
length (either 2 or 10 char tokens).
These
commands do replay, not logging. Sample
cases include:
Set_type commands call log_do_set_type;
Init/Load/Free/Dump call pr_init/pr_load/pr_free/pr_dump;
Delete calls log_do_delete;
StopCmd sets
replayState = STATE2 (exit from STATE1)
The AddRowCmd and default:
cases both check valdity of the row as a new table record, and then call
pr_add_row. This means the AddRowCmd prefix to an ordinary table row is optional;
the data row on the command line will be pr_added in either case.
The rest of the command
interpretation is in the switch(command) block of Keypress. Do_command returns the command value to
Keypress so it may be further processed in bde-specific ways. My goal is to
simplify the Keypress function and move
tt from bdeReplay.cc to pr_log.c. This
requires moving its two remaining functions elsewhere in
bde/src (not into pr_util, because genv12 produces generic database maintenance
code and doesn't know about bde's X11 GUI editing features).
(1) STATE0: move this initialization part into Keypress (STATE0) or
to pr_replay().
(2) STATE3: move the
'finalization' part of replay_log (STATE3) to
Keypress.
---------------------------------------------------------------
VI. Notes on bdeReplay:
Content Category |Approx
lines (rounded)
|
Revision History |100
Declarations | 50
replay_log() code |
85
Keypress() code | 65
Comments |200
totals |500
VII. Refactoring BDE to
use LCP and further evolution:
Bde contains embedded code
for its state machines. When bde is
re-programmed to use the LCP interpreter, genv12 will generate the
next-state-assignment code in an event dispatcher based on the STDiagram
database. The event dispatcher performs
the actual state transition just BEFORE
calling the next state action. The action itself evaluates guard conditions
(synchronous evaluation of pre-conditions for a transition when a particular
asynchronous event is dispatched.)
There are several parts to
re-factoring bde to use LCP:
(1) LCP must be upgraded to
alpha and Linux platforms.
(2) Bde must be refactored so
the state codes become more object-oriented and the actions become single function calls.
There are two long-range goals for bde:
(1) Refactor bde and LCP to
use gencpp as the code generator.
(2) Refactor bde to localize
and wrap all its X11-dependencies into functions that are GUI-independent; then
we can conditionally compile alternate GUI functions for MSWindows or Java.