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 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@280: static long Current_PLC_EvalFB; etisserant@280: static long 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@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@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@280: /* python thread is not in ? */ etisserant@280: if( PythonState & PYTHON_LOCKED_BY_PLC){ etisserant@280: /* Rising edge on TRIG */ etisserant@280: if(data__->TRIG && !data__->TRIGM1 && etisserant@280: /* and not already being processed */ etisserant@280: data__->STATE == PYTHON_FB_FREE) etisserant@280: { etisserant@280: /* Get a new line */ etisserant@280: Current_PLC_EvalFB = (Current_PLC_EvalFB + 1) %% %(python_eval_fb_count)d; 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@280: /* copy CODE in variable into BUFFER local*/ etisserant@280: data__->BUFFER = data__->CODE; 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@280: //printf("__PythonEvalFB push %%*s\n",data__->BUFFER.len, data__->BUFFER.body); etisserant@280: }else if(data__->STATE == PYTHON_FB_ANSWERED){ etisserant@280: data__->RESULT = data__->BUFFER; etisserant@280: data__->ACK = 1; etisserant@280: data__->STATE = PYTHON_FB_FREE; etisserant@280: //printf("__PythonEvalFB pop %%*s\n",data__->BUFFER.len, data__->BUFFER.body); etisserant@280: } etisserant@280: /* retain value for trig etisserant@280: * do this only when PYTHON_LOCKED_BY_PLC etisserant@280: * to avoid missed rising edge due to asynchronism */ etisserant@280: data__->TRIGM1 = data__->TRIG; 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@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@280: WaitPythonCommands(); 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: