[Pntool-developers] SF.net SVN: pntool:[244]
Brought to you by:
compaqdrew,
miordache
From: <mio...@us...> - 2011-04-02 00:08:41
|
Revision: 244 http://pntool.svn.sourceforge.net/pntool/?rev=244&view=rev Author: miordache Date: 2011-04-02 00:08:32 +0000 (Sat, 02 Apr 2011) Log Message: ----------- 2011 update--first part Modified Paths: -------------- Makefile spnbox/spnbox.h spnbox/tests/Makefile spnbox/tests/StructuredIO.c translator/pngenerator.c translator/pngenerator.g Added Paths: ----------- Makefile09 newcodegen/ newcodegen/Makefile newcodegen/ProcessTemplate.c newcodegen/SupervisorTemplate.c newcodegen/codegen.h newcodegen/compexample.c newcodegen/filltmpl.lex newcodegen/plantCompiler.c newcodegen/plantCompiler.h newcodegen/spcommon.h newcodegen/supervisorCompiler.c pnheaders09/ spnbox/disj2normal.c spnbox/disj2pn.c spnbox/disq2disj.c spnbox/tests/test-lindisj.c spnbox/tests/test-lindisj.txt spnbox/tests/test-lindisq.c spnbox/tests/test-lindisq.txt spnbox/tests/test-lintree.c spnbox/tests/test-lintree.txt spnbox/tests/test-superdis.c spnbox/tree2spec.c Removed Paths: ------------- README.txt pnheaders/ Modified: Makefile =================================================================== --- Makefile 2009-08-23 07:45:43 UTC (rev 243) +++ Makefile 2011-04-02 00:08:32 UTC (rev 244) @@ -7,7 +7,6 @@ # the object code. # # 2) Place all relevant object files and libraries in the same directory. -# Codegen note: makefile is in codegen/, however .o files are codegen/src/*.o # 3) Place no other object files in that directory (or else please make # appropriate changes below). # @@ -23,30 +22,33 @@ PNHEADERS=pnheaders SPNBOX=spnbox -CODEGEN=codegen -CODEGENOBJS = codegen/src +CODEGEN=newcodegen +CODEGENOBJS = $(CODEGEN)/obj TRANSLATOR=translator ct: objectfiles main_function.o $(COMPILER) -o ct $(PNHEADERS)/*.o $(CODEGENOBJS)/*.o $(SPNBOX)/*.o \ main_function.o $(SPNBOX)/*.a $(TRANSLATOR)/libtranslator.a + cp ct.exe .. + objectfiles: cd $(PNHEADERS); make cd $(CODEGEN); make static cd $(SPNBOX); make cd $(TRANSLATOR); make -main_function.o: $(PNHEADERS)/main_function.c $(PNHEADERS)/pns.h $(SPNBOX)/spnbox.h $(CODEGEN)/src/codegen.h +main_function.o: $(PNHEADERS)/main_function.c $(PNHEADERS)/pns.h $(SPNBOX)/spnbox.h $(CODEGEN)/codegen.h $(COMPILER) -c $(PNHEADERS)/main_function.c -Ispnbox -I$(CODEGEN)/src -I$(PNHEADERS) clean: rm -f main_function.o rm -f ct cd $(PNHEADERS); make clean - cd codegen; make clean + cd $(CODEGEN); make clean cd $(TRANSLATOR); make clean cd $(SPNBOX); make clean distclean: clean - cd codegen; make distclean - cd $(TRANSLATOR); make distclean \ No newline at end of file + cd $(CODEGEN); make distclean + cd $(TRANSLATOR); make distclean + Added: Makefile09 =================================================================== --- Makefile09 (rev 0) +++ Makefile09 2011-04-02 00:08:32 UTC (rev 244) @@ -0,0 +1,54 @@ +# This is the make file of the 2009 version of the program. +# +# Modify it so that the files you contribute are properly included. +# I propose the following format (but you may change it if necessary). +# +# 1) Create a local make file in your directory. It should generate +# the object code. +# +# 2) Place all relevant object files and libraries in the same directory. +# Codegen note: makefile is in codegen/, however .o files are codegen/src/*.o +# 3) Place no other object files in that directory (or else please make +# appropriate changes below). +# +# Please make sure all commands can be run in Linux! +# +# Changelog: +# 6/26/09 SC: Changed ct instruction to include static library files +#(.a) from the spnbox directories. This is necessary to link the lpsolve library. +# +# If you make changes to this format, please document them above. + +COMPILER=gcc -g + +PNHEADERS=pnheaders09 +SPNBOX=spnbox +CODEGEN=codegen +CODEGENOBJS = codegen/src +TRANSLATOR=translator + +ct: objectfiles main_function.o + $(COMPILER) -o ct $(PNHEADERS)/*.o $(CODEGENOBJS)/*.o $(SPNBOX)/*.o \ + main_function.o $(SPNBOX)/*.a $(TRANSLATOR)/libtranslator.a + cp ct.exe .. + +objectfiles: + cd $(PNHEADERS); make + cd $(CODEGEN); make static + cd $(SPNBOX); make + cd $(TRANSLATOR); make + +main_function.o: $(PNHEADERS)/main_function.c $(PNHEADERS)/pns.h $(SPNBOX)/spnbox.h $(CODEGEN)/src/codegen.h + $(COMPILER) -c $(PNHEADERS)/main_function.c -Ispnbox -I$(CODEGEN)/src -I$(PNHEADERS) + +clean: + rm -f main_function.o + rm -f ct + cd $(PNHEADERS); make clean + cd codegen; make clean + cd $(TRANSLATOR); make clean + cd $(SPNBOX); make clean + +distclean: clean + cd codegen; make distclean + cd $(TRANSLATOR); make distclean Deleted: README.txt =================================================================== --- README.txt 2009-08-23 07:45:43 UTC (rev 243) +++ README.txt 2011-04-02 00:08:32 UTC (rev 244) @@ -1,20 +0,0 @@ -You're looking at the SOURCE CODE REPOSITORY for pntool - -Drew Crawford administrates this repo. If you need help, send me an e-mail: dr...@se... - -Here's the directory structure: - -codegen - these files are maintained by Drew Crawford and Kris Eggert. - This will become the code generating portion of the petri net. - -pnheaders - these files are maintained by Dr. Iordache. They provide functions - for the manipulation of PN objects. - -spnbox - this code performs the supervisory control. Maintained by - Stephen Camp. Uses LPSOLVE as the MILP solver. LPSOLVE is - distributed under the LGPL licence. - -translator - this code takes a high-level-language spec and turns it into a pns datastructure. Maintained by Bion Oren. Uses ANTLR - (third-party/ANTLR3_ctarget_x_x_x/). ANTLR is distributed under the BSD license. - -Feel free to add more directories! Track your code in here! Added: newcodegen/Makefile =================================================================== --- newcodegen/Makefile (rev 0) +++ newcodegen/Makefile 2011-04-02 00:08:32 UTC (rev 244) @@ -0,0 +1,25 @@ +COMPILER=gcc -g +PNDIR=../pnheaders/ + +comp: plantCompiler.o compexample.o filltmpl.fl.o supervisorCompiler.o + make -f pnexample.mak -C$(PNDIR) + $(COMPILER) -o compexample plantCompiler.o compexample.o filltmpl.fl.o $(PNDIR)matrix.o $(PNDIR)general.o $(PNDIR)pns.o $(PNDIR)insert.o supervisorCompiler.o + +static: plantCompiler.o filltmpl.fl.o supervisorCompiler.o + cp plantCompiler.o filltmpl.fl.o supervisorCompiler.o obj/ + +compexample.o: compexample.c *.h $(PNDIR)*.h + $(COMPILER) -c -I$(PNDIR) compexample.c + +plantCompiler.o: plantCompiler.c *.h $(PNDIR)*.h + $(COMPILER) -c -I$(PNDIR) plantCompiler.c + +supervisorCompiler.o: supervisorCompiler.c *.h $(PNDIR)*.h + $(COMPILER) -c -I$(PNDIR) supervisorCompiler.c + +filltmpl.fl.o: filltmpl.lex + flex -Pfl -ofilltmpl.fl.c filltmpl.lex + $(COMPILER) -c filltmpl.fl.c + +clean: + rm *.o obj/*.o || true \ No newline at end of file Added: newcodegen/ProcessTemplate.c =================================================================== --- newcodegen/ProcessTemplate.c (rev 0) +++ newcodegen/ProcessTemplate.c 2011-04-02 00:08:32 UTC (rev 244) @@ -0,0 +1,212 @@ + +#include<stdarg.h> +#include<stdio.h> +#include<stdlib.h> +#include<time.h> +#include<string.h> +#include<pthread.h> +#include<limits.h> +#include<sys/types.h> + +#ifdef ___PROCESS +int ___debug = 0; // value determined by parameters of main function +#else +extern int ___debug; // corresponds to variable of supervisor +#endif + +#include"spcommon.h" + +static void* ___main_function(void* ___v); + +// +++++++ OTHER DECLARATIONS (GENERATED CODE HERE) + + +#ifndef ___NTR + +// Here are some default definitions allowing to compile the file without +// automatically generated code. This is for debugging purposes. + +#define ___NTR 1 // ___NTR defined in the automatically generated code +// TR represents a list of transitions and NTR its max size. +static const char ___TRinfo[10]; + +#endif + +static void* ___main_function(void* ___v) { + int ___i, ___j, ___flag, ___ntran; + time_t ___ctime; + char* ___str; + struct ___thread_data *___pthr = ___v; + struct ___trans ___TR[___NTR]; + + void *p___ = 0; // Reserved for user code. Useful when there are + // several thread copies running in parallel. + // Each thread can allocate memory and associate p___ to the + // address, if needed. + // At the end of the program p___ should be deallocated if nonzero!!! + + int ___place; // denotes the value of the current place + int ___finish; // the program terminates when its value is nonzero + + ___finish = 0; + ___flag = 0; + ___place = ___pthr->state; + + // !!! To define communication parameters ... + + time(&___ctime);//___ctime used to initialize the random number generator + // Next, ___ctime is modified so that different processes started at the + // same time do not have the same ___ctime. + // The random number generator of the threads is initialized by supervisor. +#ifndef ___THREAD + ___ctime += (int) getpid(); +#else + ___ctime += (int) getpid() + (int)___pthr->id; +#endif + srand(___ctime%64000); + + while(!___finish) { + switch(___place) { + + // +++++++ case list (GENERATED CODE HERE) + + default: + // display error message for undefined place + debugInfo("Internal ERROR: the place %d is undefined", ___place); + ___finish = 1; + } // ends main switch block + + if(___finish) + break; // exit main while loop + + //debugInfo("Calling ___NextState"); + ___place = ___NextState(___TR, ___TRinfo, ___place, ___i, &___finish, ___pthr, debugInfo); + // at this point ___i = no of elements of ___TR + //debugInfo("Going to place %d", ___place); // place = -1 when terminating + + } // ends main while loop + + ___send_exit_notification(___pthr); + + debugInfo("The end of the main function was reached"); + + return 0; +} + + +#ifndef ___THREAD + + +void ___print_args(int argc, char* argv[], struct ___thread_data *___pthr, struct ___thread_comm *___pc) { + int ___i, ___j; + char *___str, *___s1 = 0, *___s2 = 0, *___s3 = 0, *___s4 = 0; + + if(___debug) { + if(argc <= 1) + asprintf(&___s1, "The main function was entered. The main function has no parameters."); // This line should not be reached! + else { + for(___i = 0, ___j = 0; ___i < argc; ___i++) + ___j += strlen(argv[___i]); + ___j += argc + 1; + ___str = malloc(___j*sizeof(char)); + for(___i = 0, ___j = 0; ___i < argc - 1; ___i++) { + sprintf(___str + ___j, "%s ", argv[___i]); + ___j += strlen(argv[___i]) + 1; + } + sprintf(___str + ___j, "%s", argv[___i]); + asprintf(&___s1, "The process was started with the following command line\n\t%s", ___str); + free(___str); + } + if(___pthr || ___pc) { + asprintf(&___s2, "\nThe following parameters were received:\n"); + if(___pthr) + asprintf(&___s3, "id = %d debug = %d initial state = %d", (int)___pthr->id, (int)___pthr->debug, (int)___pthr->state); + if(___pc) + asprintf(&___s4, " write end = %d", (int)___pc->pwrite); + } + if(!___s2) asprintf(&___s2, " "); + if(!___s3) asprintf(&___s3, " "); + if(!___s4) asprintf(&___s4, " "); + } + debugInfo("%s%s%s%s", ___s1, ___s2, ___s3, ___s4); + free(___s1); free(___s2); free(___s3); free(___s4); +} + + +#define ___RETURN_IF_ERROR_1(___f, ___string) \ + if(!___f) { \ + ___print_args(argc, argv, ___pthr, ___pc);\ + debugInfo(___string); \ + free(___pthr); free(___pc); \ + return 1; \ + } + +#define ___RETURN_IF_ERROR_2(___f, ___string) \ + if(!___f) { \ + ___print_args(argc, argv, ___pthr, ___pc);\ + debugInfo(___string); \ + close(___pc->pread); close(___pc->pwrite); \ + ___send_exit_notification(___pthr); \ + free(___pthr); free(___pc); \ + return 1; \ + } + + +int main(int argc, char* argv[]) { + + // Update to read from pipe parameters! See ___upd_comm_param + + int ___i, ___j, ___flag = 0, ___vplace; + char* ___str; + struct ___thread_data *___pthr = 0; + struct ___thread_comm *___pc = 0; + + ___debug = 1; // allow messages if errors occur at the beginning + + ___pthr = calloc(1, sizeof(*___pthr)); + if(!___pthr) { + debugInfo("Could not allocate 'struct ___thread_data' object"); + return 1; + } + ___pc = calloc(1, sizeof(*___pc)); + if(!___pc) { + free(___pthr); + debugInfo("Could not allocate 'struct ___thread_comm' object"); + return 1; + } + + ___flag = (argc > 1); + if(___flag) + ___pc->pread = atoi(argv[1]); + // Command line parameter from get_process_comm_data in SupervisorTemplate + + ___RETURN_IF_ERROR_1(___flag, "The process exits since no command line parameter is given.\n"); + + // READ PARAMETERS FROM PIPE + + ___j = ___get_msg(___pc->pread, &___pc->pwrite, sizeof(___pc->pwrite), debugInfo); + ___RETURN_IF_ERROR_1(___j, "The process exits since 'pwrite' cannot be read from pipe.\n"); + ___j = ___get_msg(___pc->pread, &___pthr->id, sizeof(___pthr->id), debugInfo); + ___RETURN_IF_ERROR_1(___j, "The process exits since process id cannot be read from pipe.\n"); + ___j = ___get_msg(___pc->pread, &___pthr->debug, sizeof(___pthr->debug), debugInfo); + ___RETURN_IF_ERROR_2(___j, "The process exits since 'debug' cannot be read from pipe.\n"); + ___j = ___get_msg(___pc->pread, &___pthr->state, sizeof(___pthr->state), debugInfo); + ___RETURN_IF_ERROR_2(___j, "The process exits since the initial state cannot be read from pipe.\n"); + + ___pthr->comm = ___pc; + ___debug = ___pthr->debug; + + + ___print_args(argc, argv, ___pthr, ___pc); + + ___main_function(___pthr); + + debugInfo("The process is terminating.\n"); + + free(___pthr); + free(___pc); + + return 0; +} + +#endif Added: newcodegen/SupervisorTemplate.c =================================================================== --- newcodegen/SupervisorTemplate.c (rev 0) +++ newcodegen/SupervisorTemplate.c 2011-04-02 00:08:32 UTC (rev 244) @@ -0,0 +1,1096 @@ +// TO DO: check pipe size and adjust it if necessary or terminate if impossible +// enable SIGPIPE for sread pipe of supervisor. +// use fcntl for both +// The plant must use fcntl so that its write is not blocked! +// Plant: Blocking read, nonblocking write! +// +// Note: read() from pipe with O_NOBLOCK set returns 0 if the sender has closed +// its end of the pipe or -1 and errno = EAGAIN if the pipe is empty. +// +// check_for_msg()/get_msg() accounts for the possibility that read() may return less bytes +// than expected. However, some testing might be needed to ensure that the way +// check_for_msg()/get_msg() deals with this error is adequate. +// If this is not the case, some functions could be created to read and write +// bytes one by one. Then, struct proc would have two additional items: one buffer +// to hold the bytes of a message that is being received, which is filled with bytes +// of data as they arrive, and one integer indicating how many bytes are in the +// buffer. +// +// The plant should send a notification message to the supervisor when terminating. +// +// The debugInfo of the supervisor should use a lock. + +#define ___SUPERVISOR + +#include<stdarg.h> +#include<stdio.h> +#include<stdlib.h> +#include<time.h> +#include<string.h> +#include<pthread.h> +#include<unistd.h> +#include<fcntl.h> +#include<signal.h> + +#define MSG_READ_LIMIT 10 +// max number of messages read from a single process during an iteration + +char ___debug; // value determined by parameters of main function +int ___finish; // the program terminates when its value is nonzero +pid_t ___pid; +pthread_t ___tid; +FILE* ___f; +char* ___name; + +#include"spcommon.h" + +// The following is used for unnamed pipe communication between plant +// and supervisor. + +struct ___comm { + int sread, swrite; + int pread, pwrite; + char pclosed; // nonzero if pread and pwrite pipes have been closed +}; + +// The following is used in the command line when a process is started. +// The size should be updated if the communication approach is changed. +// A size of (4*sizeof(int)+1) is sufficient to represent one signed +// int-type number in text form. + +char ___com[4*sizeof(int)+1]; + +// The following is used to store transition lists in "struct proc" + +struct trlist { + int t; // transition number + int a; // action label of plant transition + //char type; // 0: if input transition, 1: if output transition +}; + +// When a plant process is created, an object of type "struct proc" +// is created. The address of this object is transmitted to the process and +// this address is included in all messages from the process to the supervisor. +// These objects are placed in three lists, as follows: +// - prlist is the list of all processes started by the supervisor. +// - wtlist[i] is a list of processes of the type i that wait for the supervisor. +// - queue is a queue list of all processes that wait for the supervisor. + +struct proc { + int type; // identifies the type of the process + int request; // id of last request (not notification!) made by the proc to the supervisor + int solved; // id of last resolved request (so solved == request when all + // requests of the process have been answered). + char atype; // the action type (permission request or firing notification) + // bit 0: 0 -- permission, 1 -- notification + // bit 1: 1 -- if part of qelist (QueueEntryList of IsPermissible) + int fireable; // the plant transition selected by the supervisor algorithms + int ntlist; // number of elements of the list below + struct trlist *tlist; // list of firable plant transitions + // All entries below must be ZERO when not used + struct proc *next_in_queue; // next element of lower priority + struct proc *prev_in_queue; // next element of higher priority + struct proc *next_in_prlist; // next element in the list of processes + struct proc *prev_in_prlist; // previous element + struct proc *next_in_wtlist; // next element in the list of processes of the + // same type that wait for a supervisor action + struct proc *prev_in_wtlist; + struct proc *next_in_qelist; + + pthread_t thread_id; // id of thread + pid_t pid; // id of forked process + + void* comm; // pointer to an unspecified communication structure, such as + // struct ___comm. +}; + +struct proc *___prlist; // pointer to the first element of the list +// wtlist is defined automatically as "struct proc *___wtlist[n];" +// wtlistLast[n] (pointer to last element of wtlist[n]) is defined the same way. + +struct proc *___first, *___last; // Queue pointers + +// When a process sends a message requesting action i, the supervisor +// examines the data associated with the action i. This data consists of +// process types that are involved in firing the transition and of supervisor +// transitions that are involved. This data is stored in 'struct ___action' +// objects (the structure is defined below). Note that a transition is fired +// if all process type requirements are met and one of the supervisor +// transitions in the list is enabled. + +// Conditions are expressed by process type lists and transition lists. +// There are npr process types: type[i] denotes the type of process i and +// weight[i] denotes the weight of the transition of process i that is part +// of the synchronization. +// There are ntr supevisor transitions having the same label as the plant +// transitions participating in the synchronization. Their indices are +// in the array t. + +// The structure allows expressing constraints in which a synchronization may +// involve either t (non-source) or t' (source tran), where t and t' belong to +// the same process. This situation may arise when t and t' have the same label. +// This is done by means of the 'disj' field. If disj[i] != 0, then one of +// the transitions itr[i] should be chosen OR one of the transitions corresp to +// oplace[disj[i]-1]. + +struct ___action { + const int ni; + const int *itype; // input proc. type, array of size ni + const int *nit; // array of size ni + const int *disj; // array of size ni; see the disj field of struct sync_element + const int **itr; // array of size ni x nit[i] for transitions + const int **iweight; // array of size ni x nit[i] + + const int no; + const int *otype; // output proc. type, array of size no + const int *not; // array of size no + const int **oweight; // array of size no x not[i] + const int **oplace; // array of size no x not[i] + // (place in which proc is started) + const int ntr; + const int *t; // array of size ntr +}; + + +// The PN structure of the supervisor is represented using the following +// structure. Each transition 0, 1, ..., n has an object of this type. + +struct ___PN { + const int nipl; // number of input places + const int *iplace; // vector of nipl elements + const int *iweight; // vector of nipl elements + + const int nopl; // number of output places + const int *oplace; // vector of nopl elements + const int *oweight; // vector of nopl elements +}; + + +// +++++++ OTHER DECLARATIONS (GENERATED CODE HERE) + +// default values included below for debugging puroposes +#ifndef n___prtypes +#define n___prtypes 1 +#endif + +struct proc *___prtype[n___prtypes]; + +int ___marking[___pnum]; // supervisor marking + + + +inline int ___is_enabled(int ___t) { // checks whether ___t is enabled + int ___i, ___nipl; + const int *___iplace, *___iweight; + const struct ___PN *___p; + + ___p = ___trans + ___t; + ___nipl = ___p->nipl; + ___iplace = ___p->iplace; + ___iweight = ___p->iweight; + + for(___i = 0; ___i < ___nipl; ___i++) + if(___marking[___iplace[___i]] < ___iweight[___i]) + break; + return (___i == ___nipl); +} + + +inline void ___fire(int ___t) { + // Fires __t without checking if it is enabled + + int ___i; + struct ___PN ___d = ___trans[___t]; + + for(___i = 0; ___i < ___d.nipl; ___i++) + ___marking[___d.iplace[___i]] -= ___d.iweight[___i]; + for(___i = 0; ___i < ___d.nopl; ___i++) + ___marking[___d.oplace[___i]] += ___d.oweight[___i]; +} + + +inline void ___fire_zltrans() { + // check 0 label transitions and fire them if enabled + + #ifdef ZERO_LABEL_TRANS + + for(___flag = 1; ___flag; ) { + ___flag = 0; + for(___i = 0; ___i < ZERO_LABEL_TRANS; ___i++) { + ___tr = ___zltrans[___i]; + if(___is_enabled(___tr)) { + ___fire(___tr); + ___flag = 1; + } + } + } + #endif +} + + +inline void ___fire_all(int ___t) { + // Fires __t without checking if it is enabled; fires also all zero label + // transitions that are enabled until none remains enabled. + + int ___i, ___flag, ___tr; + + ___fire(___t); + ___fire_zltrans(); +} + + +void ___UpdateSupervisorMarking(int ___a) { // ___a is the action number + + #ifdef ___ACTIONS + + const int *___ip = ___act[___a].t, ___n = ___act[___a].ntr; + int ___i; + + for(___i = 0; ___i < ___n; ___i++) + if(___is_enabled(___ip[___i])) { + ___fire_all(___ip[___i]); + break; + } + #endif +} + + +/* + +int find_weight(int ___prtype, int ___a, int ___t) { + // Returns zero if ___t has no input place. Otherwise, it + // returns the weight of the input arc of ___t. + + int ___i, ___j, ___w = 0; + struct ___action ___at = ___act[___a]; + + if(___at.ni) { + for(___i = 0; ___i < ___at.ni; ___i++) + if(___at.itype[___i] == ___prtype) + break; + if(___i < ___at.ni) { // that is, if ___prtype was found + for(___j = 0; ___j < ___at.nit[___i]; ___j++) + if(___t == ___at.itr[___i][___j]) { + ___w = ___at.iweight[___i][___j]; + break; + } + } + } + return ___w; +} + +*/ + + +void inline clear_qel_entries(struct proc* ___qel_last) { + // Clears from QueueEntryList all elements that follow ___qel_last + struct proc* ___pr, *___pr2; + for(___pr = ___qel_last->next_in_qelist; ___pr; ___pr = ___pr2) { + ___pr->atype -= (___pr->atype & 2); // clear bit 1 + ___pr2 = ___pr->next_in_qelist; + ___pr->next_in_qelist = 0; + } + ___qel_last->next_in_qelist = 0; +} + +struct proc* SearchQueue(int ___prtype, int ___t, int ___w, int ___cnt,\ + struct proc* ___qel_last) { + // The search is done for transitions 't' with *t!=0 corresponding to + // an action 'a' and to processes of type 'prtype'. 'w' is the weight + // of 't'. 'qel_last' denotes the last element of QueueEntryList. The + // function returns zero if nothing is found. Otherwise, it adds the + // appropriate processes to the QueueEntryList and returns the new + // last element of QueueEntryList. + + struct proc *___pr, *___qlast; + struct trlist* ___ptl; + int ___i, ___c = ___cnt; + + ___qlast = ___qel_last; + for(___pr = ___wtlist[___prtype]; ___pr; ___pr = ___pr->next_in_wtlist) { + if(___pr->atype & 2) // if entry in QueueEntryList + continue; + // Check if t is in the transition list + for(___ptl = ___pr->tlist, ___i = 0; ___i < ___pr->ntlist; ___i++) + if(___t == ___ptl[___i].t) + break; + if(___ptl) { // That is, if the transition was found + ___pr->fireable = ___t; + ___pr->atype |= 2; // set the bit 1 + ___qlast->next_in_qelist = ___pr; + ___qlast = ___pr; + ___c++; + if(___c >= ___w) + return ___qlast; + } + } + clear_qel_entries(___qel_last); // clear qel entries added by SearchQueue + return 0; +} + + +#ifdef ___MASK +inline void ___reset_mask(int ___n) { + int ___i; + for(___i = 0; ___i < ___n; ___i++) + ___mask[___i] = 0; +} +#endif + + +#ifdef ___ACTIONS // The following function called in the context of actions + +int IsPermissible(struct proc* ___q) { + // Checks whether the entry ___q of the queue has some transition that + // may be fired. It returns a nonzero number (action number + 1) if this + // is the case. + + int ___i, ___j, ___k, ___a, ___t, ___w; + int ___count, ___permissible, ___ptype, ___type; + char ___first; + struct ___action *___pa; + struct proc *___qel_last, *___q2; + + ___q->atype |= 2; // the instruction places {q} in QueueEntryList + ___ptype = ___q->type; + for(___i = 0; ___i < ___q->ntlist; ___i++) { + ___a = ___q->tlist[___i].a; + ___pa = ___act + ___a; // pa: pointer to the action element + + // Check whether any of the supervisor transitions associated with the + // action ___a is enabled. To execute ___a, at least one should be enabled. + + if(___pa->ntr) { // if there are sup. trans. associated with ___a + for(___j = 0; ___j < ___pa->ntr; ___j++) + if(___is_enabled(___pa->t[___j])) + break; // found one enabled supervisor transition + if(___j >= ___pa->ntr) + continue; // line reached if there are supervisor transitions + // associated with the action and none is enabled. + } + + // Note: For any correct supervisor, if the supervisor enables t + // it enables also all transitions with the same label as t. Thus, + // it is not necessary to check whether any other transition synchronized + // with t is supervisor enabled, since they have the same label. + + ___t = ___q->tlist[___i].t; + ___q->fireable = ___t; + ___qel_last = ___q; + ___permissible = 1; + #ifdef ___MASK + ___reset_mask(___pa->no); + #endif + for(___j = 0, ___first = 1; ___j < ___pa->ni; ___j++) { + // Note: pa->ni cannot be zero, because ___t cannot be a source + // transition (a plant will not request permission to fire one of its + // source transitions). Thus, ___t must be part of the input transition + // list of *___pa, so ni >= 1. + + ___type = ___pa->itype[___j]; + + // (a) Check first whether entry j of pa corresponds to t + // If yes, it is necessary to check the weight of t and whether enough + // many processes enable t. + + if(___first && ___ptype == ___type) { + for(___k = 0; ___k < ___pa->nit[___j]; ___k++) + if(___t == ___pa->itr[___j][___k]) + break; + if(___k < ___pa->nit[___j]) { // if the transition t was found + ___first = 0; + // If a non-source transition is chosen and source transitions are an + // alternative, an alternative source transition should not be fired + #ifdef ___MASK + ___w = ___pa->disj[___j]; + if(___w) // if source tran alternatives are present + ___mask[___w-1] = 1; // then mask (inhibit) them + #endif + ___w = ___pa->iweight[___j][___k]; + if(___w == 1) + continue; // continues with the next ___j + ___q2 = SearchQueue(___type, ___t, ___w, 1, ___qel_last); + if(___q2) { + ___qel_last = ___q2; + continue; // continues with the next ___j + } + ___permissible = 0; // line reached if t is unfirable + clear_qel_entries(___q); // clear QueueEntryList + break; // Go to next i, since t is unfirable + } // endif ___k < ... + } // endif ___first && ... + + // (b) Check whether some transition of entry j of pa is firable + + for(___k = 0; ___k < ___pa->nit[___j]; ___k++) { + ___w = ___pa->iweight[___j][___k]; + ___q2 = SearchQueue(___type, ___pa->itr[___j][___k], ___w, 0, ___qel_last); + if(___q2) { // if a firable entry was found + ___qel_last = ___q2; + // If a non-source transition is chosen and source transitions are an + // alternative, an alternative source transition should not be fired + #ifdef ___MASK + ___w = ___pa->disj[___j]; + if(___w) // if source tran alternatives are present + ___mask[___w-1] = 1; // then mask (inhibit) them + #endif + break; // Go to the next ___j + } + } // end for(___k ... + if(___k >= ___pa->nit[___j]) { // if no firable transition was found + if(___pa->disj[___j]) // if there are alternative source transitions + continue; // continue with the next ___j + clear_qel_entries(___q); //clear QueueEntryList; line reached if no sol found + ___permissible = 0; + break; // Go to next i, since t is unfirable + } + + } // end for(___j ... + if(___permissible) + return ___a + 1; // the transition may be fired! + } // end for(___i ... + ___q->atype -= ___q->atype & 2; // reset bit 1 of atype + return 0; // no transition of ___q may be fired! +} // ends IsPermissible + +#endif // ends ifdef ACTIONS + + +struct ___comm* ___new_comm_struct() { + int ___ip[2]; + struct ___comm* ___p; + + ___p = malloc(sizeof(*___p)); + if(! ___p) { + debugInfo("Could not allocate memory for 'struct ___comm' object"); + return 0; + } + + if (pipe(___ip) == -1) { + debugInfo("Could not open unnamed pipes"); + free(___p); + return 0; + } + + ___p->sread = ___ip[0]; + ___p->pwrite = ___ip[1]; + + if (pipe(___ip) == -1) { + debugInfo("Could not open unnamed pipes"); + close(___p->sread); + close(___p->pwrite); + free(___p); + return 0; + } + + ___p->swrite = ___ip[1]; + ___p->pread = ___ip[0]; + + fcntl( ___p->sread, F_SETFL, O_NONBLOCK); + fcntl( ___p->swrite, F_SETFL, O_NONBLOCK); + fcntl( ___p->pwrite, F_SETFL, O_NONBLOCK); + + return ___p; +} + + +struct proc* ___NewProcess(int ___type) { + // Creates new process structure. Places it at the beginning in prlist. + + struct proc *___p; + + ___p = calloc(1, sizeof(*___p)); // initializes everything to zero + if(!___p) { + debugInfo("Could not allocate memory for 'struct proc' object"); + return 0; + } + + ___p->comm = ___new_comm_struct(); + if(!___p->comm) { + free(___p); + return 0; + } + + ___p->tlist = calloc(___maxOutpTranNum[___type], sizeof(*(___p->tlist))); + if( ! ___p->tlist) { + debugInfo("Could not allocate memory for 'tlist' field of 'struct proc'"); + free(___p->comm); + free(___p); + return 0; + } + + ___p->ntlist = ___maxOutpTranNum[___type]; + ___p->type = ___type; + + // insert element in ___prlist + + if( ! ___prlist) + ___prlist = ___p; + else { + ___prlist->prev_in_prlist = ___p; + ___p->next_in_prlist = ___prlist; + ___prlist = ___p; + } + + return ___p; +} + + +void ___free_comm(struct ___comm *___pc) { + if(___pc) { + close(___pc->sread); close(___pc->swrite); + if( ! ___pc->pclosed ) { + close(___pc->pread); close(___pc->pwrite); + } + free(___pc); + } +} + + +void inline ___remove_from_prlist(struct proc *___p) { + if(___prlist == ___p) // is ___p first element of ___prlist? + ___prlist = ___p->next_in_prlist; + if(___p->prev_in_prlist) + ___p->prev_in_prlist->next_in_prlist = ___p->next_in_prlist; + if(___p->next_in_prlist) + ___p->next_in_prlist->prev_in_prlist = ___p->prev_in_prlist; + ___p->next_in_prlist = 0; // these two lines unnecessary if ___p is freed next + ___p->prev_in_prlist = 0; +} + +void inline ___remove_from_queue(struct proc *___p) { + if(___first == ___p) + ___first = ___p->next_in_queue; + if(___last == ___p) + ___last = ___p->prev_in_queue; + if(___p->prev_in_queue) + ___p->prev_in_queue->next_in_queue = ___p->next_in_queue; + if(___p->next_in_queue) + ___p->next_in_queue->prev_in_queue = ___p->prev_in_queue; + ___p->prev_in_queue = 0; + ___p->next_in_queue = 0; +} + +void inline ___remove_from_wtlist(struct proc *___p) { + struct proc **___ap = ___wtlist + ___p->type, **___apL = ___wtlistLast + ___p->type; + + if(*___ap == ___p) + *___ap = ___p->next_in_wtlist; + if(*___apL == ___p) + *___apL = ___p->prev_in_wtlist; + if(___p->prev_in_wtlist) + ___p->prev_in_wtlist->next_in_wtlist = ___p->next_in_wtlist; + if(___p->next_in_wtlist) + ___p->next_in_wtlist->prev_in_wtlist = ___p->prev_in_wtlist; + ___p->prev_in_wtlist = 0; + ___p->next_in_wtlist = 0; +} + + + +void ___free_proc(struct proc* ___p) { + if(___p) { + + if(___p->tlist) free(___p->tlist); + + ___free_comm(___p->comm); // Close communication channels and free memory + + // Remove ___p from process lists + + ___remove_from_prlist(___p); + ___remove_from_queue(___p); + ___remove_from_wtlist(___p); + + free(___p); + } +} + + +struct ___thread_data* get_thread_data(struct proc* ___p, int ___where) { + struct ___thread_data *___pthr; + struct ___thread_comm *___pc; + + ___pthr = calloc(1, sizeof(*___pthr)); + if(!___pthr) { + debugInfo("Could not allocate 'struct ___thread_data' object"); + return 0; + } + ___pc = calloc(1, sizeof(*___pc)); + if(!___pc) { + free(___pthr); + debugInfo("Could not allocate 'struct ___thread_comm' object"); + return 0; + } + + ___pc->pread = ((struct ___comm*) ___p->comm)->pread; + ___pc->pwrite = ((struct ___comm*) ___p->comm)->pwrite; + ___pthr->comm = ___pc; + ___pthr->debug = ___debug; + ___pthr->state = ___where; + ___pthr->id = (void*) ___p; + + return ___pthr; +} + + +void get_process_comm_data(struct proc* ___p) { + sprintf(___com, "%d", ((struct ___comm*)___p->comm)->pread); +} + + +int ___upd_comm_param(int ___sup, struct proc* ___p, int ___where) { + // This function is used for processes created via "fork" instructions. + // Since "fork" creates duplicate file descriptors in the child process, + // unnecessary descriptors are closed in this function. + + struct proc *___pa; + void *___v = ___p; + + if(!___sup) { // if this is the child process, close 'sread' and 'swrite' + close(((struct ___comm*)___p->comm)->sread); + close(((struct ___comm*)___p->comm)->swrite); + for(___pa = ___prlist; ___pa; ___pa = ___pa->next_in_prlist) { + if(___pa == ___p) + continue; + close(((struct ___comm*)___pa->comm)->sread); + close(((struct ___comm*)___pa->comm)->swrite); + } + return 1; + } + else { // if this is the supervisor process + close(((struct ___comm*)___p->comm)->pread); + close(((struct ___comm*)___p->comm)->pwrite); + ((struct ___comm*)___p->comm)->pclosed = 1; + + // send parameters to child process + + if(write(((struct ___comm*)___p->comm)->swrite, \ + &((struct ___comm*)___p->comm)->pwrite, \ + sizeof(((struct ___comm*)___p->comm)->pwrite)) == -1) + return 0; + if(write(((struct ___comm*)___p->comm)->swrite, & ___v, \ + sizeof(void*)) == -1) + return 0; + if(write(((struct ___comm*)___p->comm)->swrite, & ___debug, \ + sizeof(___debug)) == -1) + return 0; + if(write(((struct ___comm*)___p->comm)->swrite, & ___where, \ + sizeof(___where)) == -1) + return 0; + } + return 1; +} + + +inline int ___send_to_supervisor(const void* ___pdata, int ___n, \ + struct proc* ___p) { + // Sends ___n bytes of data located at address ___pdata. Here, ___p is the + // address of the 'struct proc' object of the sender process. + // This function is called only from error conditions. It returns 0 if the + // message could not be sent. + + return ___send_msg(___pdata, ___n, ((struct ___comm*)___p->comm)->pwrite, debugInfo); +} + + +void ___terminate_process(struct proc* ___p) { + if(___p->pid) // then this is a forked process + kill(___p->pid, SIGINT); + else // then this is a thread + pthread_kill(___p->thread_id, SIGINT); + ___free_proc(___p); +} + + +struct proc* ___StartProcess(int ___type, int ___where) { + struct proc *___pr; + int ___i; + struct ___thread_data *___pthr; + pid_t ___cpid; + struct ___msg ___ms; + + ___pr = ___NewProcess(___type); + if( ! ___pr) { + debugInfo("Unable to create new process!\n"); + return 0; + } + + #ifdef ___THREADS + + if(___start_fn[___type]) { + ___pthr = get_thread_data(___pr, ___where); + if(!___pthr) { + debugInfo("Cannot start thread with empty ___thread_data object"); + return 0; + } + ___i = pthread_create(&___pr->thread_id, 0, ___start_fn[___type],\ + (void*) ___pthr); + if(___i) { + debugInfo("pthread_create has returned the error code %d", ___i); + free(___pthr); + ___free_proc(___pr); + return 0; + } + return ___pr; + } + + #endif + + #ifdef ___PROCESSES + + if(___pname[___type]) { + get_process_comm_data(___pr); // updates the ___com variable + ___cpid = fork(); + if(___cpid == -1) { + debugInfo("fork() was not able to create a new process"); + ___free_proc(___pr); + return 0; + } + if( ! ___cpid) { // code execuded by child process + ___upd_comm_param(0, ___pr, ___where); + execlp(___pname[___type], ___pname[___type], ___com, 0); + debugInfo("exclp was unable to start \"%s %s\"", ___pname[___type], ___com); + ___ms.type = ___EXIT_NOTIFICATION; + ___ms.id = ___pr; + ___send_to_supervisor(&___ms, sizeof(___ms), ___pr); + exit(EXIT_FAILURE); // this line should not be reached + } + else { // code executed by supervisor + // upd_comm_param below updates the communication settings of the + // supervisor and sends input parameters to the child process + ___pr->pid = ___cpid; + if( ! ___upd_comm_param(1, ___pr, ___where)) { + debugInfo("Unable to initiate communication with child process"); + ___terminate_process(___pr); // also calls ___free_proc(___pr) + return 0; + } + return ___pr; + } + } + + #endif + + debugInfo("___StartProcess indicates a problem with the supervisor template or\ + the supervisor compiler"); // this line should not be reached + + return 0; +} + + + +void ___finish_sig_handler(int ___sig) { + struct proc* ___p; + + if(getpid() != ___pid) + exit(EXIT_FAILURE); + else if(pthread_self() != ___tid) + pthread_exit(0); + else { + debugInfo("A signal has been received and the program will be terminated"); + for(___p = ___prlist; ___p; ___p = ___p->next_in_prlist) + ___terminate_process(___p); // also calls___free_proc(___p); + exit(EXIT_SUCCESS); + } +} + + +inline int ___check_for_msg(struct proc *___p, struct ___msg *p___ms) { + // The function returns 0 if there are no messages + + return ___get_msg(((struct ___comm*)___p->comm)->sread, p___ms, \ + sizeof(*p___ms), debugInfo); +} + + +void inline ___PerformAction(int ___a, char* ___msk) { + +#ifdef ___ACTIONS + + int ___i, ___j, ___n, ___w; + struct ___action* ___ac = ___act + ___a; + + // Start new processes (if applicable) + + ___n = ___ac->no; + for(___i = 0; ___i < ___n; ___i++) { + if(___msk) + if(___msk[___i]) // ignore masked entries + continue; + ___j = 0; + if(___ac->not[___i] > 1) + ___j = rand() % ___ac->not[___i]; // select one of the source transitions + ___w = ___ac->oweight[___i][___j]; + for( ; ___w; ___w--) + ___StartProcess(___ac->otype[___i], ___ac->oplace[___i][___j]); + } + +#endif + +} + + +void ___execute_action(int ___a) { + int ___i = 1; + +#ifdef ___ACTIONS + + ___i = (___a < 0) || (___a >= ___ACTIONS); + +#endif + + if(___i) { + debugInfo("Action %d is undefined", ___a); + return; + } + + ___UpdateSupervisorMarking(___a); + ___PerformAction(___a, 0); // start new processes, if applicable +} + + +int ___process_msg(struct ___msg *p___ms) { + // Returns zero if the process of p___ms has terminated + struct proc **___ap, **___apL, *___p, *___pa = p___ms->id; + int ___i; + + switch(p___ms->type) { + + case ___EXIT_NOTIFICATION: // then deallocate process + + for(___p = ___prlist; ___p; ___p = ___p->next_in_prlist) { + if(___pa == ___p) { + ___free_proc(___p); + break; + } + } + return 0; + + case ___FIRING_REQUEST: + + + // Is this message about the last answered request? + if(___pa->solved == p___ms->req_id) // req_id may not be zero + return 1; // nothing to do, the request is already answered + + // Is this firing request already in the queue? + + if(___pa->request == p___ms->req_id) { + ___i = ___pa->ntlist; + if(___i >= ___maxOutpTranNum[___pa->type]) + debugInfo("Transition list of (%d:%d) exceeds maximum size", (int) ___pa->pid, (int) ___pa->thread_id); + else { + ___pa->tlist[___i].t = p___ms->t; + ___pa->tlist[___i].a = p___ms->a; + ___pa->ntlist++; + } + debugInfo("Proc. type %d (%d:%d): t%d (a%d) alternative tran. to fir. req. %d", (int)___pa->type, (int) ___pa->pid, (int) ___pa->thread_id, (int)p___ms->t, (int)p___ms->a, (int)___pa->request); + return 1; + } + + // If not in queue, add to queue and wtlist + + ___pa->request = p___ms->req_id; + + ___pa->tlist[0].t = p___ms->t; + ___pa->tlist[0].a = p___ms->a; + ___pa->ntlist = 1; + + if(!___last) { // update queue + ___last = ___pa; + ___first = ___pa; + } + else { + ___last->next_in_queue = ___pa; + ___pa->prev_in_queue = ___last; + ___last = ___pa; + } + + ___ap = ___wtlist + ___pa->type; // update wtlist + ___apL = ___wtlistLast + ___pa->type; + if(!*___apL) { + *___apL = ___pa; + *___ap = ___pa; + } + else { + (*___apL)->next_in_wtlist = ___pa; + ___pa->prev_in_wtlist = *___apL; + *___apL = ___pa; + } + + debugInfo("Firing req. [id:%d] (%d:%d): proc. type %d requests t%d (a%d)", (int)___pa->request, (int) ___pa->pid, (int) ___pa->thread_id, (int) ___pa->type, (int)p___ms->t, (int)p___ms->a); + + return 1; + + case ___FIRING_NOTIFICATION: + + ___execute_action(p___ms->a); + return 1; + + default: + debugInfo("Type %d of (%d:%d) is an undefined message type", (int)p___ms->type, (int) ___pa->pid, (int) ___pa->thread_id); + } + + return 1; +} + + + +inline void ___send_permission_msg(struct proc *___p) { + + ___send_msg(&___p->fireable, sizeof(int), ((struct ___comm*)___p->comm)->swrite, debugInfo); + +} + + +int main(int argc, char* argv[]) { + int ___i, ___j, ___a; + char* ___str, ___no_msg, ___start_flag; + struct proc *___p, *___pa, *___pn; + struct sigaction ___act; + struct ___msg ___ms; + time_t ___ctime; + char ___help_msg[] = "\nOptions: \n\ + -e: suppress error and status messages\n\ + -s: start the program right away without displaying introductory message\n\n\ + Ctrl-C (SIGINT) can be used to stop the program. This will terminate all\n\ + its processes.\n"; + + ___pid = getpid(); + ___tid = pthread_self(); + + ___debug = 0; + ___finish = 0; + ___f = stderr; + ___name = 0; + ___prlist = 0; + ___first = 0; ___last = 0; + + // install signal handlers + + sigaction(SIGINT, 0, &___act); // initialize act + ___act.sa_flags = 0; + //sigemptyset(&act.sa_mask); // no signals blocked + ___act.sa_handler = ___finish_sig_handler; + sigaction(SIGINT, &___act, 0); // initialize act + + time(&___ctime); + srand(___ctime%64000); + + ___debug = 1; + ___start_flag = (argc <= 1); + for(___i = 1; ___i < argc; ___i++) { + if(!strcmp(argv[___i], "-e")) + ___debug = 0; + else if(!strcmp(argv[___i], "-h") || !strcmp(argv[___i], "--help")) { + fprintf(stdout, ___help_msg); + return 0; + } + else if(!strcmp(argv[___i], "-s")) { + ___start_flag = 0; // "-s" is for future use; unnecessary right now + } + } + + if(___start_flag) { + fprintf(stdout, "%s\nThe program will start in five seconds.\n", ___help_msg); + sleep(5); + } + + if(___debug) { + if(argc <= 1) + debugInfo("The main function was entered. The main functions has no parameters."); + else { + for(___i = 0, ___j = 0; ___i < argc; ___i++) + ___j += strlen(argv[___i]); + ___j += argc + 1; + ___str = malloc(___j*sizeof(char)); + for(___i = 0, ___j = 0; ___i < argc - 1; ___i++) { + sprintf(___str + ___j, "%s ", argv[___i]); + ___j += strlen(argv[___i]) + 1; + } + sprintf(___str + ___j, "%s", argv[___i]); + debugInfo("The process was started with the following command line\n\t%s", ___str); + free(___str); + } + } + + // initialize supervisor ___marking + memset(___marking, 0, sizeof(*___marking)*___pnum); + for(___i = 0; ___i < n___m0; ___i++) + ___marking[___m0[___i][0]] = ___m0[___i][1]; + ___fire_zltrans(); // fire zero label transitions of supervisor if enabled + + // start processes + for(___i = 0; ___i < n___pm0; ___i++) + for(___j = 0; ___j < ___pm0[___i][2]; ___j++) // ___pm0[___i][2]: # of tokens + ___StartProcess(___pm0[___i][0], ___pm0[___i][1]); // arguments: type, place + + + while( ! ___finish) { + + if(!___prlist) + break; // all processes have terminated, so the supervisor terminates + + // Check all processes for new messages + + for(___p = ___prlist, ___no_msg = 1; ___p; ___p = ___pn) { + ___pn = ___p->next_in_prlist; // process_msg below may deallocate ___p + for(___i = 0; ___i < MSG_READ_LIMIT; ___i++) { + ___j = ___check_for_msg(___p, &___ms); + if( ! ___j ) + break; // exit loop, since there are no messages + ___no_msg = 0; + if( ! ___process_msg(&___ms) ) // processes the message + break; // exit loop since the process of ___p has terminated + } + } + + if(___no_msg) { + sleep(2); // any interrupt will awake the process + continue; + } + + // Check for queued requests that can be resolved + +#ifdef ___ACTIONS // There may be queued requests only if actions are defined, + // as can be seen in the definition of ___process_msg(). + + for(___p = ___first; ___p; ___p = ___pn) { // search queued requests + ___pn = ___p->next_in_queue; // the loop will not deallocate ___p but + // it may remove ___p from the queue + ___a = IsPermissible(___p); // creates EntryList (qelist) + if(___a > 0) { // then the request of ___p can be resolved + + // Grant permission to fire to the processes in EntryList + // Remove also from queue and wait list processes in EntryList + + for(___pa = ___p; ___pa; ___pa = ___pa->next_in_qelist) { + // This loop does not affect 'qelist' (EntryList). + // However, it does affect 'queue' and 'wtlist'. + if(___pn == ___pa) // if ___pn will be removed from queued requests + ___pn = ___pn->next_in_queue; // select next queued request + ___send_permission_msg(___pa); + ___remove_from_queue(___pa); // remove ___pa from queued requests + ___remove_from_wtlist(___pa); + } + + clear_qel_entries(___p); // delete EntryList except for first element + ___p->atype -= (___p->atype & 2); // remove first el of EntryList + ___a--; // since IsPermissible adds 1 to the return value + ___UpdateSupervisorMarking(___a); // update supervisor marking +#ifdef ___MASK + ___PerformAction(___a, ___mask); // start new processes, if applicable +#else + ___PerformAction(___a, 0); // start new processes, if applicable +#endif + + } + } +#endif // end of ifdef ___ACTIONS + + } // end of while( ! ___finish) (the main loop) + + debugInfo("The end of the main function was reached.\n"); + + return 1; +} + Added: newcodegen/codegen.h =================================================================== --- newcodegen/codegen.h (rev 0) +++ newcodegen/codegen.h 2011-04-02 00:08:32 UTC (rev 244) @@ -0,0 +1,51 @@ +/* codegen.h + + This header file was created for the 2010 version of the code generation tools. +*/ + +/* The code generation module expects processes satisfying the following + + - Nondeterministic places: If a place is nondeterministic and one of its + output transitions is controlled by the supervisor or participates in a + process synchronization, then permission to fire is always requested + from the supervisor. (NOT YET IMPLEMENTED.) + + - The supervisor format: It is assumed that each transition has a + unique label. Process transitions that are synchronized are assumed to + have the same label. The supervisor is connected to the plant using the + conventional parallel composition of PNs. + + - The select function: If defined, it should return the number of + transitions selected. Let n be that number. The select function writes the + list of enabled transitions by writing their data to the first n elements + of the array ___TR of ProcessTemplate.c. + + It is assumed that for a pn structure, the select[i] item contains a + string indicating how the select function should be called. Example: + select[0] = "myselect(1, x, y);", where x and y should be defined in + the context of the segment[i] code. Note that select[0] does not specify + any assignment of the return value. The code generation module is + responsible for writing the assignment of the return value. + +*/ + + + +#define INDENT 2 // indentation increment used in generated C code + +#define DBG3 is_verbose()>=3 + + +int FillTemplate(FILE* template, FILE* outp, ...); // in filltmpl.lex + +FILE* searchTemplate(char* s); // in plantCompiler.c + +char* GetFileName(const char* s, const char *ext); +// Similar to "asprintf(&x, "%s.%s", s, ext); return x;" + +void compileSupervisor(pns *supervisor, char* sname, process **procTypes, \ + int nbrTypes, TrData** TrInfo, struct synclist* sl, \ + const char* buildparam); + +void compileProcessArray(process **procArray, int nbrElements, TrData** TrInfo, \ + int** actlabels); Added: newcodegen/compexample.c =================================================================== --- newcodegen/compexample.c (rev 0) +++ newcodegen/compexample.c 2011-04-02 00:08:32 UTC (rev 244) @@ -0,0 +1,278 @@ +#include"pns.h" +#include"codegen.h" + + +int verb = 3; + +int inline is_verbose() { return verb; } + +int main() { + int i; + + /* Declaring pn objects */ + + pns pn_p, pn_s, pn_s2, pn, pn2, pn3; + process pr1, pr2, pr3, *apr[2]; + + + /* Petri nets can be initialized using the incidence matrix */ + + /* This will be used for the incidence matrix of one pn */ + + int Dp[] = { 1, 1, -1, 1, 0, 0,\ + 0, 0, 1, 0, -1, 0,\ + 0, -1, 1, 0, 0, -1,\ + -1, 0, 0, 0, 1, 0,\ + 0, 0, 0, -1, 0, 1}; + + int mp[] = { 1, 1, 1, 1, 1}; /* The initial marking */ + + /* These will be used for the input and output matrices of another pn */ + + int Is[] = { 0, 1, 0, 0,\ + 0, 0, 1, 0,\ + 0, 0, 0, 1,\ + 1, 0, 0, 0}; + + int Os[] = { 0, 0, 1, 0,\ + 0, 0, 0, 1,\ + 1, 0, 0, 0,\ + 0, 1, 0, 0}; + + int ms[] = {0, 0, 1, 1}; /* The initial marking */ + + struct placelist *plist; + arcs* alist; + char *str; + + /* Here is how the pn objects are created */ + + /* The following creates a pn of 5 places, 6 transitions, incidence + matrix Dp, and initial marking mp */ + pn_p = createpn("pnum tnum D m0", 5, 6, Dp, mp); + + /* Here the input and output matrices are used to initialize the pn */ + pn_s = createpn("pnum tnum I O m0", 4, 4, Is, Os, ms); + + /* This creates a pn object without transition arcs */ + pn = createpn("pnum tnum", 3, 5); + + int m0pn[] = {1, 0, 0}; + updatepn(&pn, "m0", m0pn); + + // Let's create the following PN: + // + // t2 + // -------|<---- + // | p0 p1 |p2 + // ->O->|->O->|->O<-- + // | t0 | t1 | + // | | | + // | V | + // | --- t3 | + // | | + // ------>|------- + // t4 + + /* GetMatrixEl and SetMatrixEl can be used to read or write the elements + of a matrix. */ + + /* For instance, arcs could be added by changing the value of the input + and output matrices of the pn */ + + SetMatrixEl(&pn.out, 0, 0, 1); /* add arc from p0 to t0 of weight 1 */ + SetMatrixEl(&pn.out, 0, 4, 1); /* add arc from p0 to t4 of weight 1 */ + SetMatrixEl(&pn.in, 0, 2, 1); + + SetMatrixEl(&pn.out, 1, 1, 1); + SetMatrixEl(&pn.out, 1, 3, 1); + SetMatrixEl(&pn.in, 1, 0, 1); + + SetMatrixEl(&pn.out, 2, 2, 1); + SetMatrixEl(&pn.in, 2, 1, 1); + SetMatrixEl(&pn.in, 2, 4, 1); + + int labls[] = {-1, -2, -3, -4, -5}; + updatepn(&pn, "labels", labls); + + // Let's create one more PN: + // + // p0 p1 + // |->O->|->O->| + // t2 t0 t1 + // + + pn3= createpn("pnum tnum", 2, 3); + int labl2[] = {-1, -7, -5}; + + SetMatrixEl(&pn3.out, 0, 0, 1); /* add arc from p0 to t0 of weight 1 */ + SetMatrixEl(&pn3.in, 0, 2, 1); + + SetMatrixEl(&pn3.out, 1, 1, 1); + SetMatrixEl(&pn3.in, 1, 0, 1); + + updatepn(&pn3, "labels", labl2); + + /* For instance, to check whether there is an arc from t1 to p2: */ + i = GetMatrixEl(&pn.in, 2, 1); + if(i) + fprintf(stderr, "\nThe arc (t1, p2) has the weight %d.\n", i); + else + fprintf(stderr, "\nThere is no arc from t1 to p2.\n"); + + /* To display in text format a Petri net object use displaypn */ + + fprintf(stderr, "\n** PN_P *************************************\n"); + displaypn(pn_p, stderr); /* displays pn_p */ + fprintf(stderr, "\n** PN_S *************************************\n"); + displaypn(pn_s, stderr); /* displays pn_s */ + fprintf(stderr, "\n** PN *************************************\n"); + displaypn(pn, stderr); /* displays pn */ + + /* Properties of the pn objects can be set or changed with updatepn */ + + updatepn(&pn, "place_name", 0, "start_p"); + /* The line above associates the name "start_p" with the place 0 */ + + updatepn(&pn, "trans_name", 0, "tr0"); + /* The line above associates the name "tr0" with the transition 0 */ + + updatepn(&pn, "select", 1, "f1(u)"); /*select function 'f1' for place 1*/ + + updatepn(&pn, "segment", 0, "u++; printf(\"\\nPN: State 0 u = %d\", u);\ndelay(1);"); + updatepn(&pn, "segment", 1, "printf(\"\\nPN: State 1\");\ndelay(1);"); + updatepn(&pn, "segment", 2, "printf(\"\\nPN: State 2\");\ndelay(1);"); + + updatepn(&pn3, "segment", 0, "u++; printf(\"\\nPN3: State 0 u = %d\", u);\ndelay(2);"); + updatepn(&pn3, "segment", 1, "printf(\"\\nPN3: State 1\");\ndelay(2);"); + + /* Text transmitted by means of createpn/updatepn is copied to the + corresponding items of the pns object; thus it should be + deallocated, when applicable. For instance: */ + + //str = tcalloc(strlen("f1(i, j);")+1, sizeof(char)); + //strcpy(str, "f1(i, j);"); + //updatepn(&pn, "segment", 1, str); + ///* Now, str may be freed, since its content has been copied */ + // free(str); + + /* Flags are set as follows. */ + + updatepn(&pn, "unobservable nondeterministic", 0, 1); + /* marks transition 0 unobservable and place 1 as nondeterministic */ + + updatepn(&pn, "live uncontrollable uncontrollable", 0, 0, 2); + /* The line above sets the "live" and "uncontrollable" flags of + transition 0 and the uncontrollable flag of transition 2 */ + + /* To create a copy */ + + pn2 = copypn(&pn); + + + + /* Arcs are added similarly (using createpn or updatepn). Here is + an example. */ + + plist = tmalloc(sizeof(struct placelist)); + plist->place = 1; + plist->next = tmalloc(sizeof(*plist)); + plist->next->place = 2; + plist->next->next = 0; + + alist = tmalloc(sizeof(arcs)); + alist->in_place = 0; + alist->out_places = plist; + alist->condition = tcalloc(strlen("j == i || z == 0.1")+1, sizeof(char)); + strcpy(alist->condition, "j == i || z == 0.1"); + alist->next = 0; + + updatepn(&pn, "arcs", 0, alist); /* arc list associated with transition 0 */ + + /* The line above updates the arc_list item of the pns object as well as the + input & output matrices. */ + + /* Note the absence of the lines of code deallocating alist. The object alist is + placed in the pns object, it is not copied. Thus, alist is deallocated when + the pns object is deallocated with the function deallocpn. */ + + + TrData **TInf; + + // Let's create also some processes + + asprintf(&(pr1.name), "type_one"); asprintf(&(pr2.name), "PN3"); + pr1.instance = "pr1"; pr2.instance = "pr2"; + pr1.thread = 0; pr2.thread = 1; + pr1.pn = &pn2; pr2.pn = &pn3; + asprintf(&(pr1.build), "gcc -g -o $$$.exe $$$.c"); + pr2.build = 0; + // pr2.build = "make -f pn3.mak"; + pr1.include = "#include<stdio.h>\nint pr1_state = 0, i, j, u = 0;\n\n\ +void delay(int n) {\n time_t a, b;\n\ + for(time(&a), time(&b); a + n > b; time(&b));\n}\n\nint f1(int z) {\n\ + if(u > 5) {\n\ + ___TR[0].no_output = 1;\n\ + ___TR[0].label = -4;\n\ + ___TR[0].trans = 3;\n\ + }\n\ + else {\n\ + ___TR[0].no_output = 0;\n\ + ___TR[0].label = -2;\n\ + ___TR[0].trans = 1;\n\ + ___TR[0].place = 2;\n\ + }\n\ + return 1;\n\ +}\n"; + pr2.include = "#include<stdio.h>\nint pr1_state = 0, i, j, u = 0;\n\n\ +void delay(int n) {\n time_t a, b;\n\ + for(time(&a), time(&b); a + n > b; time(&b));\n}\n\n"; + + pr1.type = 0; pr1.start = 1; pr2.type = 1; pr2.start = 0; + apr[0] = &pr1; + apr[1] = &pr2; + + TInf = calloc(sizeof(TrData*), 2); + TInf[0] = calloc(sizeof(TrData), pn2.tnum); + TInf[1] = calloc(sizeof(TrData), pn3.tnum); + + // int labls[] = {-1, -2, -3, -4, -5}; + // int labl2[] = {-1, -7, -5}; + + struct synclist *sn1; + struct sync_element *se; + + sn1 = calloc(2, sizeof(*sn1)); + se = calloc(10, sizeof(*se)); + + sn1[0].next = sn1+1; sn1->s = &se[0]; + se[0].prtype = 0; se[0].n = 1; + int t1[] = {0}, w1[] = {1}; + se[0].t = t1; se[0].w = w1; se[0].next = &se[1]; + se[1].prtype = 1; se[1].t = t1; se[1].w = w1; se[1].n = 1; + sn1[1].s = &se[2]; + int t2[] = {4}; + se[2].prtype = 0; se[2].t = t2; se[2].w = w1; se[2].n = 1; + sn1[1].o = &se[3]; se[3].n = 1; + int t3[] = {2}, p1[] = {0}; + se[3].prtype = 1; se[3].t = t3; se[3].w = w1; se[3].p = p1; + + //compileProcessArray(apr, 2, TInf); + //fprintf(stderr, "\nNow generating the supervisor ..."); + //compileSupervisor(&pn2, "spr", apr, 2, TInf, sn1, 0); + + CodeGenerator(&pn2, "spr", apr, 2, sn1, TInf, 0); + + for(i = 0; i < 2; i++) + free(TInf[i]); + free(TInf); + free(se); free(sn1); free(pr1.name); free(pr2.name); + /* To deallocate a pn object use deallocpn */ + + deallocpn(&pn_p); + deallocpn(&pn_s); + deallocpn(&pn); + deallocpn(&pn2); + + return 0; +} Added: newcodegen/filltmpl.lex =================================================================== --- newcodegen/filltmpl.lex (rev 0) +++ newcodegen/filltmpl.lex 2011-04-02 00:08:32 UTC (rev 244) @@ -0,0 +1,92 @@ + +LABEL ([ \t]*\/\/[ \t]*[+]{3,}[[:print:]]*) +IDENTIFIER ("(GENERATED CODE HERE)") + +%{ + #include<stdio.h> + #include<stdarg.h> + + int flag, skip, i, j; + va_list param; + FILE *out; + char *stmp, cc; + +%} + + +%% + +{LABEL}/{IDENTIFIER} { + flag = 0; skip = 1; + fprintf(out, "%s (BEGINNING OF CODE)\n", yytext); + for(i = 0; yytext[i] == ' ' || yytext[i] == '\t'; i++); + cc = yytext[i]; + yytext[i] = 0; // g... [truncated message content] |