Interpreting command streams

Previous: Our oh-so-complex output stream ] [ Top: wftk core index ] [ Next: Loading the datasheet for an active process ]

The command stream is simply a list of notifications of events which have occurred. The engine must open the datasheet for the process involved, find the procdef which applies, and then move down the command list, making appropriate changes to the datasheet as defined in the procdef. As this is done, output lines are being written to communicate back to the task manager what things have been changes.

Most particularly, when the procdef tells the engine that it is time to activate a task, the engine must stop and notify the task manager. This is simply because the task manager must assign the task instance a unique identifier. Once this is done, the task manager must call the core engine again to active the task, because the datasheet must reflect the task ID of the new task. (I think.)
 
See Loading the datasheet for an active process
void interpret (COMMAND * list)
{
   FILE * temp;
   int  datasheet_dirty;
   char line[1024];
   char * mark;
   XML * holder;

   datasheet_dirty = 0;

   while (list) {
      if (!strcmp (list->name, "start")) {
         See 'start' command
      } else if (!strcmp (list->name, "complete")) {
         See 'complete' command
      } else if (!strcmp (list->name, "reject")) {
         See 'reject' command
      } else if (!strcmp (list->name, "setvalue")) {
         See 'setvalue' command
      } else if (!strcmp (list->name, "script")) {
         See 'script' command
      } else {
         output ('E', "Unknown command %s encountered", list->name);
      }
skip_command:
      list = list->next;
   }

   if (datasheet != NULL && datasheet_dirty) {
      sprintf (sbuf, "%s%s", DATASHEET_REPOSITORY, process);
      temp = fopen (sbuf, "w");
      if (!temp) {
         output ('E', "Can't write to datasheet for process %s.", process);
      } else {
         xml_write (temp, datasheet);
         fclose (temp);
      }
   }
}


'start' command
Starting a process consists of these steps: Of these steps, the last, writing the datasheet, is taken care of centrally. If the datasheet_dirty flag is set, then the datasheet will be written when the interpreter finishes.

So let's find our process definition. The procdef identifier we're given is a general name; we have to find the current version of that procdef and mark that version as the one we're actually using. Otherwise if changes are made, things are going to get rapidly out of whack.

So in the process definition repository, there is a file procdef_versions.txt for procdef ID 'procdef'. That consists of lines of text, the first tab-delimited field of which is the number of a version. The last line, therefore, identifies the current version.

For version 2a of procdef procdef, the actual definition XML will be in the file procdef_2a.xml. That's what we'll write into our datasheet as our governing procdef.

And yeah, I'm using a goto here. Sue me.
 
if (procdef != NULL) {
   output ('E', "Start command must be first.  Skipping command.");
   goto skip_command;
}

if (list->argc < 1) {
   sprintf (sbuf, "%s%s", DATASHEET_REPOSITORY, process);
   temp = fopen (sbuf, "r");
   if (!temp) {
      output ('E', "No process '%s' exists.", process);
      return;
   }

   datasheet = xml_read (temp);
   fclose (temp);

   sprintf (sbuf, "%s%s", PROCESS_DEFINITION_REPOSITORY, xml_attrval (datasheet, "procdef"));
} else {
   sprintf (sbuf, "%s%s_versions.txt", PROCESS_DEFINITION_REPOSITORY, list->argv[0]);
   temp = fopen (sbuf, "r");
   if (!temp) {
      output ('E', "No process '%s' defined.", list->argv[0]);
      return;
   }

   *line = '\0';
   while (!feof (temp)) { fgets(line, 1023, temp); }
   fclose (temp);
   mark = strchr (line, '\t');
   if (mark) *mark = '\0';

   sprintf (sbuf, "%s%s_%s.xml", PROCESS_DEFINITION_REPOSITORY, list->argv[0], line);
}

temp = fopen (sbuf, "r");
if (!temp) {
   output ('E', "Process definition version file '%s' is missing.", sbuf);
   return;
}

procdef = xml_read(temp);
fclose (temp);

if (datasheet == NULL) {
   datasheet = xml_create("datasheet");
   sprintf (sbuf, "%s_%s.xml", list->argv[0], line);
   xml_set(datasheet, "procdef", sbuf);
}

datasheet_dirty = 1;

output ('N', xml_attrval (procdef, "name"));
output ('O', xml_attrval (procdef, "author"));
holder = xml_loc (procdef, "workflow.note[description]");
if (holder != NULL) {
   xml_writecontent (stdout, holder);
}
printf ("\nEOF\n");

queue_procdef (procdef);
process_procdef();


'complete' command
To complete a task, we:
 
load_datasheet();
mark = strrchr (list->argv[0], ':');
if (mark) mark++;
else (mark = list->argv[0]);

sprintf (sbuf, "queue.item[%s]", mark);
holder = xml_loc (queue, sbuf);
if (holder) xml_set (holder, "block", "resume");
datasheet_dirty = 1;
process_procdef();


'reject' command
Rejection of a task is pretty much the same as completion, except that we invoke exception handlers.

'setvalue' command
To set a value, we simply:
 


'script' command
The script command allows us to use command files (or stdin) to signify several events at once. Note that all commands in a single run must be applied to the same process, and that process is specified on the command line. The commands to be found in a script file lack the process ID, because it's already been specified.

In keeping with the idea that these commands are really state transitions, we will simply tack commands from script files onto the end of the current list. Thus we really don't care that scripts run in order -- all this is conceptually happening simultaneously.
 
temp = NULL;
if (list->argc) {
   output ('D', "Script %s", list->argv[0]);
   temp = fopen (list->argv[0], "r");
   if (!temp) { output ('E', "Unable to open script file '%s'", list->argv[0]); }
} else {
   output ('D', "Script on stdin");
   temp = stdin;
}

if (temp) {
   output ('D', "This is where the script would be loaded.", list->argv[0]);
   if (temp != stdin) fclose (temp);
}
Previous: Our oh-so-complex output stream ] [ Top: wftk core index ] [ Next: Loading the datasheet for an active process ]


This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2000, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license.