//********************************************************** // File : bdeReplay.cc // Description : // Created by : 02s592 // Routines : replay_log // // Uses: encode_token and do_command in pr_log.c // Edit history: // // $Log: bdeReplay.cc,v $ // Revision 1.1.2.19 2004/04/03 03:28:54 lechner // replayState initialized; aded comments. // // Revision 1.1.2.18 2003/10/22 07:10:46 lechner // Major additions to comments and note preambles before eliminating replay_log fcn. // // Revision 1.1.2.17 2003/10/14 01:45:41 lechner // Replaced assert(hcg_log == 2); //==0 failed 021028 - delta TBD: 0 ---> 2 by // #ifdef DEBUG printf("\nbdeReplay:l206: hcg_log value is %d\n", hcg_log);#endif // // Revision 1.1.2.16 2003/08/08 04:55:15 lechner // Changed assert(hcg_log == 0) to assert(hcg_log == 2) // // Revision 1.1.2.15 2002/10/26 05:33:40 lechner // Added a comment circa :280, and ain #ifdef BDELOG #Qendif pair there. // // Revision 1.1.2.14 2002/09/29 05:35:26 lechner // Minor comments; fixed dprint..() calls // // Revision 1.1.2.13 2002/09/27 05:26:51 lechner // Moved updatedisplaylist to fileio.cc. // // Revision 1.1.2.12 2002/09/21 06:24:52 lechner // Removed "assert(command == StartCmd); //<<< 2 by // Added the rcs line '// #ifdef DEBUG printf("\nbdeReplay:l206: hcg_log value is %d\n", hcg_log);#endif // Added the rcs line '// // Added the rcs line '// Revision 1.1.2.16 2003/08/08 04:55:15 lechner // Added the rcs line '// Changed assert(hcg_log == 0) to assert(hcg_log == 2) // Added the rcs line '// // Added the rcs line '// Revision 1.1.2.15 2002/10/26 05:33:40 lechner // Added the rcs line '// Added a comment circa :280, and ain #ifdef BDELOG #Qendif pair there. // Added the rcs line '// // Added the rcs line '// Revision 1.1.2.14 2002/09/29 05:35:26 lechner // Added the rcs line '// Minor comments; fixed dprint..() calls // Added the rcs line '// // Added the rcs line '// Revision 1.1.2.13 2002/09/27 05:26:51 lechner // Added the rcs line '// Moved updatedisplaylist to fileio.cc. // Added the rcs line '// // Added the rcs line '// Revision 1.1.2.12 2002/09/21 06:24:52 lechner // Added the rcs line '// Removed "assert(command == StartCmd); //<<< in bdeReplay.cc? // replay_log is called FROM the Xt callback routine in fileio.cc //#include //#include #include /* see if this helps avoid g++ errors ingraph.h */ //#include /* TBD: CHeck if ReDraw is a callback wrapper or the real stuff -RJL */ #include #include #include #include /* "94sbde_schema.h" is #included in graph.h - RJL 031024 */ #include "graph.h" //class i'faces; #includes state.h #include "bde.h" #include "prototype.h" // no longer declares replay_log-RJL 020501 /* #include "dprint.h" - in schema.h - RL 031024 */ // enumeration of command names as integers: typedef enum {SetIntCmd,SetFltCmd,SetKeyCmd,SetStrCmd,WaitCmd,DeleteCmd, StartCmd,FreeCmd,InitCmd,LoadCmd,StopCmd,ViewNameCmd,AddRowCmd } replayCode; extern "C" { /* in pr_load.c: */ void hcg_parse(char*, char*, int*); extern char* viewname; /* view --> viewname - RJL 020501 */ /* in pr_log.c: */ replayCode encode_token(char *token); replayCode do_command(char*, char*, int*); void hcg_read_next(void); extern hcg_key HG_pkey; extern char *logfile; extern FILE* logfile_fp; extern char token[BUFSIZE]; extern int near_realtime; extern int take_endsnapshot; extern int replayState; /* 3 state cases */ extern int replayStatus;/* return code */ extern int idx; } /* extern "C" */ //Functions: replay_log and client Keypress callback; // replayState values: (should be enum'd) #define STATE0 0 #define STATE1 1 #define STATE2 2 replayCode command; // for switch(command) extern void updatedisplaylist(struct HG *cHG); // in fileio.cc /************************************************************/ /* Function:replay_log */ /* */ /* This is the callback function to parse one logfile line */ /* and execute the action of the log entry. */ /* The arguments are saved by pr_replay for reference here */ /* Arguments: logfile - logfile to replay */ /* near_realtime - flag */ /* 1 => replay in near realtime */ /* 0 => not real time */ /* Return values: replayStatus = pr_replay() in load_data */ /* 0 - success parsing and replay */ /* -3 - cannot open file */ /* -5 - logfile doesn't start with line "SR" */ /* -7 - logfile doesn't end with line "SP" */ /* "SR" is a precondition if pr_load delegates to pr_replay.*/ /* "SP" is a post-condition that is not fatal */ /* fopen success is a pre-condition - pr_load already tried.*/ /* Effects: */ /* hcg_buffer - altered */ /* hcg_ascii_fp - altered */ /* hcg_t - altered */ /* value of database may be altered based on commamnds in */ /* the format of the data file. If no SP was found, */ /* replay_log will exit [with no change? - RJL] */ /************************************************************/ /****************************************************************** * Design Notes from RJL - 020429: most replay_log code * can be auto-generated by genv12; only the state machine and * updatedisplaylist and ReDraw calls are bde-specific. * The rest depends only on schema.h. and chgen can handle it. * * Therefore I refactored out some code and moved it to pr_util. * THis includes the Level 2 command interpreter. * Level 1 is a switch(state) as below; * Level 2 does encode(token, &command) and switch(command); * encode_token() enumerates the commands for switch(command). * * The last 5 lines of each command case block were bde-specific * and common to most but not all commands; I factored them out * into one of the two switch(command) blocks below. * TBD: Move these into Keypress(), do AFTER replay_log returns * Some lines below call other pr_util library functions * generated in pr_log.c by chgen. Looping is done in STATE1 ************************************************************* */ extern "C" int replay_log(void); // needed for pr_log.c client - RJL 020506 int replay_log(void) // RJL: void { // Valid command values depend on replayState: // Read command BEFORE each STATE# action: replayStatus = 0; /* default - only set to <0 below - RJL 040329 */ switch(replayState) { case STATE0: /* Initial state: logfile reopen check, hcg_ascii_fp = logfile_fp; * read "SR"; command = encode_token("SR"), replayState = STATE1 ; * Pre-conditions: pr_load has detected "SR" and called pr_replay to * set hcg_log = 2 and load the checkpoint file *DB1.dat. * TBD: check if client pr_replay can do this State0 action * (except for changing replayState to 1). */ dprints("replayState STATE0: logfile name is %s\n", logfile); logfile_fp = fopen(logfile, "r"); //<<< NOTE extern logfile<<< assert (logfile_fp !=NULL); //Already seen in pr_load() assert(hcg_log == 2); //!=0 on 021028 - changed 0 to 2; dprintd("\nbdeReplay:l206: hcg_log value is %d\n", hcg_log); hcg_ascii_fp = logfile_fp; hcg_read_next(); /* read first line of log file */ assert(!feof(hcg_ascii_fp)); // pre-condition: non-empty idx = 0; hcg_parse(hcg_buffer,token,&idx); //token = "SR" pre-condition is verified by pr_load. command = encode_token(token); // in pr_log.c if (command != StartCmd) { printf("Command is %d, expected StartCmd = %d\n", command, StartCmd); return -5; } else dprint("Found StartReplay command\n"); replayState = STATE1; // GO TO STATE 1 next break; // STATE0 case STATE1: /* calls do_command in pr_log.c */ /* pre-condition: file is non-empty; STATE0 token was "SR" */ hcg_read_next(); // get next command if (feof(hcg_ascii_fp)) { // no more commands (not even "SP") replayState = STATE2; break; } command = do_command(hcg_buffer, token, &idx); /* in pr_log.c */ /* do case switch(command) now - */ /* do_command calls encode_token and interprets command */ /* i.e. does the requested 'model' update commands and */ /* returns this command for a second switch(command) below */ /* which does bde-specific 'view' updates. */ /* pr_log.c contains encode_token and do_command functions */ /* TBD: make genv12 produce pr_log.c and pr_load.c - RJL 020429 */ /* The code below is bde-specific - it redraws the display after * do_command interpreted the command and updated the database * Note: Only pr_add/delete-row commands on HGcurr need to call * updatedisplaylist() before ReDraw; set_field commands do not. * TBD: Further discriminate in switch(command) actions below. RJL 020621 * (possibly by splitting STATE2 , in a bde-specific way). * or by making use of MVState suggestions below. */ /** TBD - RJL 020506: ** Only redraw if the replay update cmd is in the HGcurr displayed ** WARNING: need 2 separate XXcurr ptrs, for replay and for editing??? ** This begs for a Model View state variable in each row. ** It may be non-persistent like RFLAG (not in schema.sch) ** but it might be useful to log it (in schema.sch). ** It needs one bit to assert database up to date, ** another to assert graph_object display is up to date. ** These bits might propagate to other affected objects ** e.g.from HN to HL but not vice-versa. ** We can call these the Model and View bits of MVstate. ** Database updates from a replay source set MVstate=1 ** Display list updates from local edits set MVstate=2 ** State = 3 when both are up to date, 0 when neither is. ** A state model should update MVstate as needed. ** do_command can change the Model bit ** bde edit ops can change the View bit. ** ReDraw can check bits to selectively redraw. ** MVstate > 1 is a pre-condition for updatedisplaylist+ReDraw. ** MVstate = 1 is a post-condition from replay_log. ** NOTE: MVstate use requires RFLAG = 1 and hcg_log == 2. ** [pr_set_* macros in *schema.h write changes to logfile ** iff ( (tblptr)->RFLAG==1 && hcg_log ==1 ).] ** ** WARNING:(RJL031103): I originally thought ** that pr_create sets RFLAG== 0 (do NOT log pr_set's) ** and that pr_add sets RFLAG back to 1 (OK to log changes). ** BUT RFLAG is NEVER initialized or set in pr_util or src. ** pr_load both reads and pr_adds one row as an atomic operation. ** If bde also pr_creates/sets/adds each row indivisibly ** (as a non-interruptable state action) then RFLAG is not needed. ** ** updatedisplaylist/ReDraw sets MVstate = 3 for the ** HGrow selected for display only. ** Prefixing MVState to RFLAG gives a 3-bit state. ** NOTE RJL 020621: Replay of a multi-graph snapshot ** in fileDB1.dat requires special treatment: ** HGcurr must be restored to the playback diagram ** after any browsing using GraphSelect. Perhaps ** to be safe we need to disable replay_log if Graph-Select ** is allowed, and vice versa?. ** ** Select alters HGcurr, while HGcurr != HGreplay ** I.e. check HGcurr against a saved HGreplay copy ** if HGrowcount > 1. My recommended solution is ** to log and replay Graph-Select commmands. ** (For DBDE, this is an awareness option - ** Other users' mouse icon disappears if they ** select anogther HG to view. - RJL 031018) ** More generally, remote or logged changes can be ** applied to any/all graphs in any order, while being ** viewed or not. Immediate or lazy updates of such ** changes might be a user option. ** Changes to one diagram will be visible iff it is ** selected; a user might be notified if others change. **/ /* This first case group updates and redraws the display List: */ /* The StopCmd "SP" might come first, last, or never. */ switch(command) { case SetFltCmd: case SetIntCmd: case SetKeyCmd: case SetStrCmd: case AddRowCmd: case DeleteCmd: /* TBD: Move this case block into Keypress, AFTER replay_log returns * Then all of replay_log can be in pr_load instead of here. * Brute-force Redraw for every update is inefficient. * doCommand must report enough for bde to select update* and Redraw. * E.g. what command type was executed on what HGcontainer/diagram. * * Update RJL031019: Moved this block to Keypress below. (Goal is * to make replay_log a no-op except for parts in pr_log and Keypress.) */ /* Protect graphobjectlist's HGid and HG* from DB replay updates; * In fact, ignore display of updated object is in another HG than * the one updated remotedly. - RJL 040404 * In fact, here is one use of the access macros: reachup to HG ancestor * to check if displayed graph is being altered during replay: * iff (HGdisplay == HGreplay) * log commands need display update and vice versa. * TBD: Does graphobjectlist have a top entry with id = cHG? */ #ifdef BDELOG clearObjects(); HG_pkey = HGcurr->HGid; /* where used? - RJL 031018 */ updatedisplaylist(HGcurr); ReDraw(); #endif /* Update RJL 031018: pr_log.c:2059: pr_replay now includes * the call retval = replay_log(), * (conditional on #ifdef GENLOG, else retval = 0;) - RJL 030720 */ /* The above STATE1 commands update the database inside pr_load/log. * E.g., in pr_log.c, doCommand case setFltCmd calls log_do_set_flt * which updates a field of type float; * This bdeReplay case block calls ReDraw to display the updated diagram. * TBD: CHeck that the updated HGcurr row is the one displayed . * (DBDE replay may update another HG-row instead.) * It looks like ONLY case STATE0 code is bde-specific. * For the gencpcp test case in 2kf522/KMiu, replay_log and logfile * generate make errors. TBD: Move all except this block to pr_log.c * and making an #ifdef BDELOG-dependent call to extern * #else a no-op default might cure this inconsistency. - RJL 021007. * A client without bde's X11-callback must call replay_log another way. * #ifndef BDELOG, pr_log calls replay_log directly. RJL 021025 */ case WaitCmd: case StartCmd: case FreeCmd: case InitCmd: case LoadCmd: case ViewNameCmd: break; // no-op: no need to redraw - RJL 020506 /* OOPS: What about LoadCmd? TBD: check if LoadCmd follow thru on its own: * New .dat file loaded ==> SelectHG or DefaultHG-row display?-RJL031019 * ALso check if tail-recursive pr_load-->pr_replay-->pr_load is legal * or needs to be prohibited while logging. What about MVState? */ case StopCmd: // Split out for state update - RJL 031019 replayState = STATE2; // pre-empt Keypress - replay_log discovered this break; default: dprintd("Invalid command value %d; ignored \n", command); } // end 2nd switch(command) break; // end STATE1 case STATE2: // The token "SP" may have been detected in STATE1; // Its command is StopCmd; its absence is non-fatal;, // If we get here "SP" was already read, or is missing and feof = true, // If ("SP" and NOT feof), there is 'garbage' after "SP" to warn about. // (Garbage without "SP" is ignored while in STATE1.) // No need to read_next ; pre-condition: token is "SP" or feof. if (feof(hcg_ascii_fp)) { printf("Stop command missing - closing logfile %s \n", logfile); replayStatus = -7; } else { assert (command == StopCmd); int linecnt = 0; while (!feof(hcg_ascii_fp)) { ++linecnt; hcg_read_next(); } if (linecnt>0) // warn of junk after "SP" command printf("Ignored %d lines after \"SP\" command.\n", linecnt); replayStatus = -9; } // we got here with "SP" EOF, or EOF but no "SP", or "SP" plus junk fclose(hcg_ascii_fp); //was logfile_fp; hcg_log = 0; // allows more logging or replay I/O. // take_endsnapshot is 4th arg to pr_replay.c: // we didn't check it in pr _log:do_command (STATE1) // because infile was still open. // Since it is not bde-specific, replay_log client pr_load could do it. /* TBD: Put this also into keypress after replay_log returns? */ if (take_endsnapshot==1) { /* take final snapshot */ char* tempvar; sprintf(tempvar,"%sDB3.dat",logfile); pr_dump(viewname, tempvar,0,"w"); //view->viewname RJL 020501 } break; // end of STATE2 default: printf("Illegal value %d for replayState\n", replayState); break; } // end switch(replayState) //return replayState; /* to pr_load or X11 callback, not pr_replay - RJL */ /* TBD: Should error code signal callback to quit? */ /* Should replayStatus >= 0 become PASS replayState? */ return replayStatus; /* to client: either pr_replay, or Keypress callback */ } // end replay_log in bdeReplay.cc /********************************************************************* * Function : Keypress * * Description: Callback routine for Keypress - Calls replay_log * function iff the key pressed is 's' (for 'Step') * and ReplayReady is 1. * * Created by : Sathya Jaganathan, 02s592 * Moved from fileio.cc to bdeReplay.cc by RJL - 031018 ********************************************************************/ /* I made this a no-op UNLESS BDELOG is #defined - RJL 020924 */ 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(); /* replay_log executes ALL COMMANDS - BEFORE THE switch(replayState) below. * If it succeeds (replayStatus==0), then the switch() below is entered. */ if (replayStatus < 0) { /* 0 if PASS, negative if FAIL */ printf("Keypress: replay_log returns error code %d,ignored\n", replayStatus); } else { switch(replayState) { /*New - RJL 031019: State sequence: (0 1* 2) */ case STATE0: /* "SR" command */ replayState = STATE1; break; // added 040404 case STATE1: /* while not EOF do_command under Keypress control */ /* HERE is where bde's ReDraw should be called, after replay_log - RJL 031018 */ /* THis may be mandatory to make DBUpdate and HG-ReDraw an atomic operation */ /* OTOH, SKIP ReDraw if replay is NOT for this graph (HGreplay!=HGdisplay) */ /* So, perhaps replayState = 1 needs to be split: * 1.00: on entry, (no replay chianges and no local edits yet) * 1.10: replay_log has updated the DB last. * 1.100: replay_log has NOT changed the displayed HG) (graphobjectlist not altered) * 1.101: HGreplay==HGdisplay) (DB updates affect the displayed HG) * 1.11: updatedisplaylist HAS been called; ReDraw must be called also. * TBD: call ReDraw inside updatedisplaylist (minimize bde-specific code here) * 1.01: local edits ahve updted the canvas; (TBD: update the DB and log changes?) * Problem: currently , local edits incrementally affect the DB as wel as displaylist. * 1.1: Local edits have propagated to the DB, and also have been logged. * * NOTE: DB changes could be tagged as locally updated, THEN logged? * ALT: make a complete local copy of just the HGdisplay content. * This is less efficient, more cludgy; why does graphobjectlist need to * point to clones of its HGdisplay components, not the DB copies? * Perhaps to insluate the two so that backtrack is easier (2-phase commit): * replay log affects the DB first; local editing affects displaylist first. * EIther can update the other as a fast operation if each object on display list * and (ALL or only HGdisplay-ed?) DB objects have their own OLC state values?. */ 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