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