py_ext/plc_python.c
changeset 721 ecf4d203c4d4
parent 717 1c23952dbde1
child 728 e0424e96e3fd
equal deleted inserted replaced
720:6be032177e2a 721:ecf4d203c4d4
       
     1 /*
       
     2  * Python Asynchronous execution code
       
     3  *
       
     4  * PLC put python commands in a fifo, respecting execution order
       
     5  * with the help of C pragmas inserted in python_eval FB code
       
     6  *
       
     7  * Buffer content is read asynchronously, (from non real time part),
       
     8  * commands are executed and result stored for later use by PLC.
       
     9  *
       
    10  * In this implementation, fifo is a list of pointer to python_eval
       
    11  * function blocks structures. Some local variables have been added in
       
    12  * python_eval interface. We use those local variables as buffer and state
       
    13  * flags.
       
    14  *
       
    15  * */
       
    16 
       
    17 #include "iec_types_all.h"
       
    18 #include "POUS.h"
       
    19 #include <string.h>
       
    20 
       
    21 /* The fifo (fixed size, as number of FB is fixed) */
       
    22 static PYTHON_EVAL* EvalFBs[%(python_eval_fb_count)d];
       
    23 /* Producer and consumer cursors */
       
    24 static int Current_PLC_EvalFB;
       
    25 static int Current_Python_EvalFB;
       
    26 
       
    27 /* A global IEC-Python gateway state, for use inside python_eval FBs*/
       
    28 static int PythonState;
       
    29 #define PYTHON_LOCKED_BY_PYTHON 0
       
    30 #define PYTHON_LOCKED_BY_PLC 1
       
    31 #define PYTHON_MUSTWAKEUP 2
       
    32 #define PYTHON_FINISHED 4
       
    33 
       
    34 /* Each python_eval FunctionBlock have it own state */
       
    35 #define PYTHON_FB_FREE 0
       
    36 #define PYTHON_FB_REQUESTED 1
       
    37 #define PYTHON_FB_PROCESSING 2
       
    38 #define PYTHON_FB_ANSWERED 3
       
    39 
       
    40 int WaitPythonCommands(void);
       
    41 void UnBlockPythonCommands(void);
       
    42 int TryLockPython(void);
       
    43 void UnLockPython(void);
       
    44 void LockPython(void);
       
    45 
       
    46 int __init_%(location)s()
       
    47 {
       
    48 	int i;
       
    49 	/* Initialize cursors */
       
    50 	Current_Python_EvalFB = 0;
       
    51 	Current_PLC_EvalFB = 0;
       
    52 	PythonState = PYTHON_LOCKED_BY_PYTHON;
       
    53 	for(i = 0; i < %(python_eval_fb_count)d; i++)
       
    54 		EvalFBs[i] = NULL;
       
    55   return 0;
       
    56 }
       
    57 
       
    58 void __cleanup_%(location)s()
       
    59 {
       
    60 	PythonState = PYTHON_FINISHED;
       
    61 	UnBlockPythonCommands();
       
    62 }
       
    63 
       
    64 void __retrieve_%(location)s()
       
    65 {
       
    66 	/* Check Python thread is not being
       
    67 	 * modifying internal python_eval data */
       
    68 	PythonState = TryLockPython() ?
       
    69 	                PYTHON_LOCKED_BY_PLC :
       
    70 	                PYTHON_LOCKED_BY_PYTHON;
       
    71 	/* If python thread _is_ in, then PythonState remains PYTHON_LOCKED_BY_PYTHON
       
    72 	 * and python_eval will no do anything */
       
    73 }
       
    74 
       
    75 void __publish_%(location)s()
       
    76 {
       
    77 	if(PythonState & PYTHON_LOCKED_BY_PLC){
       
    78 		/* If runnig PLC did push something in the fifo*/
       
    79 		if(PythonState & PYTHON_MUSTWAKEUP){
       
    80 			/* WakeUp python thread */
       
    81 			UnBlockPythonCommands();
       
    82 		}
       
    83 		UnLockPython();
       
    84 	}
       
    85 }
       
    86 /**
       
    87  * Called by the PLC, each time a python_eval
       
    88  * FB instance is executed
       
    89  */
       
    90 void __PythonEvalFB(int poll, PYTHON_EVAL* data__)
       
    91 {
       
    92 	/* detect rising edge on TRIG to trigger evaluation */
       
    93 	if(((__GET_VAR(data__->TRIG) && !__GET_VAR(data__->TRIGM1)) ||
       
    94 	   /* polling is equivalent to trig on value rather than on rising edge*/
       
    95 	    (poll && __GET_VAR(data__->TRIG) )) &&
       
    96 	    /* trig only if not already trigged */
       
    97 	    __GET_VAR(data__->TRIGGED) == 0){
       
    98 		/* mark as trigged */
       
    99 	    __SET_VAR(data__->, TRIGGED, 1);
       
   100 		/* make a safe copy of the code */
       
   101 		__SET_VAR(data__->, PREBUFFER, __GET_VAR(data__->CODE));
       
   102 	}
       
   103 	/* retain value for next rising edge detection */
       
   104 	__SET_VAR(data__->, TRIGM1, __GET_VAR(data__->TRIG));
       
   105 
       
   106 	/* python thread is not in ? */
       
   107 	if( PythonState & PYTHON_LOCKED_BY_PLC){
       
   108 		/* if some answer are waiting, publish*/
       
   109 		if(__GET_VAR(data__->STATE) == PYTHON_FB_ANSWERED){
       
   110 			/* Copy buffer content into result*/
       
   111 			__SET_VAR(data__->, RESULT, __GET_VAR(data__->BUFFER));
       
   112 			/* signal result presece to PLC*/
       
   113 			__SET_VAR(data__->, ACK, 1);
       
   114 			/* Mark as free */
       
   115 			__SET_VAR(data__->, STATE, PYTHON_FB_FREE);
       
   116 			/* mark as not trigged */
       
   117 			if(!poll)
       
   118 			    __SET_VAR(data__->, TRIGGED, 0);
       
   119 			/*printf("__PythonEvalFB pop %%d - %%*s\n",Current_PLC_EvalFB, data__->BUFFER.len, data__->BUFFER.body);*/
       
   120 		}else if(poll){
       
   121 			/* when in polling, no answer == ack down */
       
   122 		    __SET_VAR(data__->, ACK, 0);
       
   123 		}
       
   124 		/* got the order to act ?*/
       
   125 		if(__GET_VAR(data__->TRIGGED) == 1 &&
       
   126 		   /* and not already being processed */
       
   127 		   __GET_VAR(data__->STATE) == PYTHON_FB_FREE)
       
   128 		{
       
   129 			/* Enter the block in the fifo
       
   130 			 * Don't have to check if fifo cell is free
       
   131 			 * as fifo size == FB count, and a FB cannot
       
   132 			 * be requested twice */
       
   133 			EvalFBs[Current_PLC_EvalFB] = data__;
       
   134 			/* copy into BUFFER local*/
       
   135 			__SET_VAR(data__->, BUFFER, __GET_VAR(data__->PREBUFFER));
       
   136 			/* Set ACK pin to low so that we can set a rising edge on result */
       
   137 			if(!poll){
       
   138 				/* when not polling, a new answer imply reseting ack*/
       
   139 			    __SET_VAR(data__->, ACK, 0);
       
   140 			}else{
       
   141 				/* when in polling, acting reset trigger */
       
   142 			    __SET_VAR(data__->, TRIGGED, 0);
       
   143 			}
       
   144 			/* Mark FB busy */
       
   145 			__SET_VAR(data__->, STATE, PYTHON_FB_REQUESTED);
       
   146 			/* Have to wakeup python thread in case he was asleep */
       
   147 			PythonState |= PYTHON_MUSTWAKEUP;
       
   148 			/*printf("__PythonEvalFB push %%d - %%*s\n",Current_PLC_EvalFB, data__->BUFFER.len, data__->BUFFER.body);*/
       
   149 			/* Get a new line */
       
   150 			Current_PLC_EvalFB = (Current_PLC_EvalFB + 1) %% %(python_eval_fb_count)d;
       
   151 		}
       
   152 	}
       
   153 }
       
   154 
       
   155 char* PythonIterator(char* result)
       
   156 {
       
   157 	char* next_command;
       
   158 	PYTHON_EVAL* data__;
       
   159 	//printf("PythonIterator result %%s\n", result);
       
   160     /*emergency exit*/
       
   161     if(PythonState & PYTHON_FINISHED) return NULL;
       
   162 	/* take python mutex to prevent changing PLC data while PLC running */
       
   163 	LockPython();
       
   164 	/* Get current FB */
       
   165 	data__ = EvalFBs[Current_Python_EvalFB];
       
   166 	if(data__ && /* may be null at first run */
       
   167 	    __GET_VAR(data__->STATE) == PYTHON_FB_PROCESSING){ /* some answer awaited*/
       
   168 	   	/* If result not None */
       
   169 	   	if(result){
       
   170 			/* Get results len */
       
   171 	   	    __SET_VAR(data__->, BUFFER, strlen(result), .len);
       
   172 			/* prevent results overrun */
       
   173 			if(__GET_VAR(data__->BUFFER, .len) > STR_MAX_LEN)
       
   174 			{
       
   175 			    __SET_VAR(data__->, BUFFER, STR_MAX_LEN, .len );
       
   176 				/* TODO : signal error */
       
   177 			}
       
   178 			/* Copy results to buffer */
       
   179 			strncpy((char*)__GET_VAR(data__->BUFFER, .body), result, __GET_VAR(data__->BUFFER,.len));
       
   180 	   	}else{
       
   181 	   	    __SET_VAR(data__->, BUFFER, 0, .len);
       
   182 	   	}
       
   183 		/* remove block from fifo*/
       
   184 		EvalFBs[Current_Python_EvalFB] = NULL;
       
   185 		/* Mark block as answered */
       
   186 		__SET_VAR(data__->, STATE, PYTHON_FB_ANSWERED);
       
   187 		/* Get a new line */
       
   188 		Current_Python_EvalFB = (Current_Python_EvalFB + 1) %% %(python_eval_fb_count)d;
       
   189 		//printf("PythonIterator ++ Current_Python_EvalFB %%d\n", Current_Python_EvalFB);
       
   190 	}
       
   191 	/* while next slot is empty */
       
   192 	while(((data__ = EvalFBs[Current_Python_EvalFB]) == NULL) ||
       
   193 	 	  /* or doesn't contain command */
       
   194 	      __GET_VAR(data__->STATE) != PYTHON_FB_REQUESTED)
       
   195 	{
       
   196 		UnLockPython();
       
   197 		/* wait next FB to eval */
       
   198 		//printf("PythonIterator wait\n");
       
   199 		if(WaitPythonCommands()) return NULL;
       
   200 		/*emergency exit*/
       
   201 		if(PythonState & PYTHON_FINISHED) return NULL;
       
   202 		LockPython();
       
   203 	}
       
   204 	/* Mark block as processing */
       
   205 	__SET_VAR(data__->, STATE, PYTHON_FB_PROCESSING);
       
   206 	//printf("PythonIterator\n");
       
   207 	/* make BUFFER a null terminated string */
       
   208 	__SET_VAR(data__->, BUFFER, 0, .body[__GET_VAR(data__->BUFFER, .len)]);
       
   209 	/* next command is BUFFER */
       
   210 	next_command = (char*)__GET_VAR(data__->BUFFER, .body);
       
   211 	/* free python mutex */
       
   212 	UnLockPython();
       
   213 	/* return the next command to eval */
       
   214 	return next_command;
       
   215 }
       
   216