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