etisserant@280: /*
etisserant@280:  * Python Asynchronous execution code
etisserant@280:  * 
etisserant@280:  * PLC put python commands in a fifo, respecting execution order
etisserant@280:  * with the help of C pragmas inserted in python_eval FB code 
etisserant@280:  * 
etisserant@280:  * Buffer content is read asynchronously, (from non real time part), 
etisserant@280:  * commands are executed and result stored for later use by PLC.
etisserant@280:  * 
etisserant@280:  * In this implementation, fifo is a list of pointer to python_eval
etisserant@280:  * function blocks structures. Some local variables have been added in
etisserant@280:  * python_eval interface. We use those local variables as buffer and state
etisserant@280:  * flags.
etisserant@280:  * 
etisserant@280:  * */
etisserant@280: 
etisserant@280: #include "iec_types_all.h"
etisserant@280: #include "POUS.h"
etisserant@280: #include <string.h>
etisserant@280: 
etisserant@280: /* The fifo (fixed size, as number of FB is fixed) */
etisserant@280: static PYTHON_EVAL* EvalFBs[%(python_eval_fb_count)d];
etisserant@280: /* Producer and consumer cursors */
etisserant@281: static int Current_PLC_EvalFB;
etisserant@281: static int Current_Python_EvalFB;
etisserant@280: 
etisserant@280: /* A global IEC-Python gateway state, for use inside python_eval FBs*/
etisserant@280: static int PythonState;
etisserant@280: #define PYTHON_LOCKED_BY_PYTHON 0
etisserant@280: #define PYTHON_LOCKED_BY_PLC 1
etisserant@280: #define PYTHON_MUSTWAKEUP 2
etisserant@286: #define PYTHON_FINISHED 4
etisserant@280:  
etisserant@280: /* Each python_eval FunctionBlock have it own state */
etisserant@280: #define PYTHON_FB_FREE 0
etisserant@280: #define PYTHON_FB_REQUESTED 1
etisserant@280: #define PYTHON_FB_PROCESSING 2
etisserant@280: #define PYTHON_FB_ANSWERED 3
etisserant@280: 
etisserant@280: int WaitPythonCommands(void);
etisserant@280: void UnBlockPythonCommands(void);
etisserant@280: int TryLockPython(void);
etisserant@280: void UnLockPython(void);
etisserant@280: void LockPython(void);
etisserant@280: 
etisserant@280: void __init_python()
etisserant@280: {
etisserant@280: 	int i;
etisserant@280: 	/* Initialize cursors */
etisserant@280: 	Current_Python_EvalFB = 0;
etisserant@280: 	Current_PLC_EvalFB = 0;
etisserant@280: 	PythonState = PYTHON_LOCKED_BY_PYTHON;
etisserant@280: 	for(i = 0; i < %(python_eval_fb_count)d; i++)
etisserant@280: 		EvalFBs[i] = NULL;
etisserant@280: }
etisserant@280: 
etisserant@280: void __cleanup_python()
etisserant@280: {
etisserant@286: 	PythonState = PYTHON_FINISHED;
etisserant@286: 	UnBlockPythonCommands();
etisserant@280: }
etisserant@280: 
etisserant@280: void __retrieve_python()
etisserant@280: {
etisserant@280: 	/* Check Python thread is not being 
etisserant@280: 	 * modifying internal python_eval data */
etisserant@280: 	PythonState = TryLockPython() ? 
etisserant@280: 	                PYTHON_LOCKED_BY_PLC : 
etisserant@280: 	                PYTHON_LOCKED_BY_PYTHON;
etisserant@280: 	/* If python thread _is_ in, then PythonState remains PYTHON_LOCKED_BY_PYTHON 
etisserant@280: 	 * and python_eval will no do anything */
etisserant@280: }
etisserant@280: 
etisserant@280: void __publish_python()
etisserant@280: {
etisserant@280: 	if(PythonState & PYTHON_LOCKED_BY_PLC){
etisserant@280: 		/* If runnig PLC did push something in the fifo*/
etisserant@280: 		if(PythonState & PYTHON_MUSTWAKEUP){
etisserant@280: 			/* WakeUp python thread */
etisserant@280: 			UnBlockPythonCommands();
etisserant@280: 		}
etisserant@280: 		UnLockPython();
etisserant@280: 	}
etisserant@280: }
etisserant@280: /**
etisserant@280:  * Called by the PLC, each time a python_eval 
etisserant@280:  * FB instance is executed
etisserant@280:  */
etisserant@280: void __PythonEvalFB(PYTHON_EVAL* data__)
etisserant@280: {
etisserant@281: 	/* detect rising edge on TRIG */
etisserant@281: 	if(data__->TRIG && !data__->TRIGM1 && data__->TRIGGED == 0){
etisserant@281: 		/* mark as trigged */
etisserant@281: 		data__->TRIGGED = 1;
etisserant@281: 		/* make a safe copy of the code */
etisserant@281: 		data__->PREBUFFER = data__->CODE;
etisserant@281: 	}
etisserant@281: 	/* retain value for next detection */
etisserant@281: 	data__->TRIGM1 = data__->TRIG;
etisserant@281: 
etisserant@280: 	/* python thread is not in ? */
etisserant@280: 	if( PythonState & PYTHON_LOCKED_BY_PLC){
etisserant@281: 		/* got the order to act */
etisserant@281: 		if(data__->TRIGGED == 1 &&
etisserant@280: 		   /* and not already being processed */ 
etisserant@280: 		   data__->STATE == PYTHON_FB_FREE) 
etisserant@280: 		{
etisserant@280: 			/* Enter the block in the fifo
etisserant@280: 			/* Don't have to check if fifo cell is free
etisserant@280: 			 * as fifo size == FB count, and a FB cannot 
etisserant@280: 			 * be requested twice */
etisserant@280: 			EvalFBs[Current_PLC_EvalFB] = data__;
etisserant@281: 			/* copy into BUFFER local*/
etisserant@281: 			data__->BUFFER = data__->PREBUFFER;
etisserant@280: 			/* Set ACK pin to low so that we can set a rising edge on result */
etisserant@280: 			data__->ACK = 0;
etisserant@280: 			/* Mark FB busy */
etisserant@280: 			data__->STATE = PYTHON_FB_REQUESTED;
etisserant@280: 			/* Have to wakeup python thread in case he was asleep */
etisserant@280: 			PythonState |= PYTHON_MUSTWAKEUP;
etisserant@281: 			//printf("__PythonEvalFB push %%d - %%*s\n",Current_PLC_EvalFB, data__->BUFFER.len, data__->BUFFER.body);
etisserant@281: 			/* Get a new line */
etisserant@281: 			Current_PLC_EvalFB = (Current_PLC_EvalFB + 1) %% %(python_eval_fb_count)d;
etisserant@280: 		}else if(data__->STATE == PYTHON_FB_ANSWERED){
etisserant@281: 			/* Copy buffer content into result*/
etisserant@280: 			data__->RESULT = data__->BUFFER;
etisserant@281: 			/* signal result presece to PLC*/
etisserant@280: 			data__->ACK = 1;
etisserant@281: 			/* Mark as free */
etisserant@280: 			data__->STATE = PYTHON_FB_FREE;
etisserant@281: 			/* mark as not trigged */
etisserant@281: 			data__->TRIGGED = 0;
etisserant@281: 			//printf("__PythonEvalFB pop %%d - %%*s\n",Current_PLC_EvalFB, data__->BUFFER.len, data__->BUFFER.body);
etisserant@280: 		}
etisserant@280: 	}
etisserant@280: }
etisserant@280: 
etisserant@280: char* PythonIterator(char* result)
etisserant@280: {
etisserant@280: 	char* next_command;
etisserant@280: 	PYTHON_EVAL* data__;
etisserant@280: 	//printf("PythonIterator result %%s\n", result);
etisserant@280: 	/* take python mutex to prevent changing PLC data while PLC running */
etisserant@280: 	LockPython();
etisserant@280: 	/* Get current FB */
etisserant@280: 	data__ = EvalFBs[Current_Python_EvalFB];
etisserant@280: 	if(data__ && /* may be null at first run */
etisserant@280: 	   data__->STATE == PYTHON_FB_PROCESSING){ /* some answer awaited*/
etisserant@280: 	   	/* If result not None */
etisserant@280: 	   	if(result){
etisserant@280: 			/* Get results len */
etisserant@280: 			data__->BUFFER.len = strlen(result);
etisserant@280: 			/* prevent results overrun */
etisserant@280: 			if(data__->BUFFER.len > STR_MAX_LEN)
etisserant@280: 			{
etisserant@280: 				data__->BUFFER.len = STR_MAX_LEN;
etisserant@280: 				/* TODO : signal error */
etisserant@280: 			}
etisserant@280: 			/* Copy results to buffer */
etisserant@280: 			strncpy(data__->BUFFER.body, result, data__->BUFFER.len);
etisserant@280: 	   	}else{
etisserant@280: 	   		data__->BUFFER.len = 0;
etisserant@280: 	   	}
etisserant@280: 		/* remove block from fifo*/
etisserant@280: 		EvalFBs[Current_Python_EvalFB] = NULL;
etisserant@280: 		/* Mark block as answered */
etisserant@280: 		data__->STATE = PYTHON_FB_ANSWERED;
etisserant@280: 		/* Get a new line */
etisserant@280: 		Current_Python_EvalFB = (Current_Python_EvalFB + 1) %% %(python_eval_fb_count)d;
etisserant@281: 		//printf("PythonIterator ++ Current_Python_EvalFB %%d\n", Current_Python_EvalFB);
etisserant@280: 	}
etisserant@280: 	/* while next slot is empty */
etisserant@280: 	while(((data__ = EvalFBs[Current_Python_EvalFB]) == NULL) || 
etisserant@280: 	 	  /* or doesn't contain command */ 
etisserant@280: 	      data__->STATE != PYTHON_FB_REQUESTED)
etisserant@280: 	{
etisserant@280: 		UnLockPython();
etisserant@280: 		/* wait next FB to eval */
etisserant@286: 		//printf("PythonIterator wait\n");
etisserant@280: 		WaitPythonCommands();
etisserant@286: 		/*emergency exit*/
etisserant@286: 		if(PythonState & PYTHON_FINISHED) return NULL;
etisserant@280: 		LockPython();
etisserant@280: 	}
etisserant@280: 	/* Mark block as processing */
etisserant@280: 	data__->STATE = PYTHON_FB_PROCESSING;
etisserant@280: 	//printf("PythonIterator\n");
etisserant@280: 	/* make BUFFER a null terminated string */
etisserant@280: 	data__->BUFFER.body[data__->BUFFER.len] = 0;
etisserant@280: 	/* next command is BUFFER */
etisserant@280: 	next_command = data__->BUFFER.body;
etisserant@280: 	/* free python mutex */
etisserant@280: 	UnLockPython();
etisserant@280: 	/* return the next command to eval */
etisserant@280: 	return next_command;
etisserant@280: }
etisserant@280: