etisserant@280: /* etisserant@280: * Python Asynchronous execution code greg@329: * etisserant@280: * PLC put python commands in a fifo, respecting execution order greg@329: * with the help of C pragmas inserted in python_eval FB code greg@329: * greg@329: * Buffer content is read asynchronously, (from non real time part), etisserant@280: * commands are executed and result stored for later use by PLC. greg@329: * 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. greg@329: * 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@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 greg@329: 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: { greg@329: /* Check Python thread is not being etisserant@280: * modifying internal python_eval data */ greg@329: PythonState = TryLockPython() ? greg@329: PYTHON_LOCKED_BY_PLC : etisserant@280: PYTHON_LOCKED_BY_PYTHON; greg@329: /* 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: /** greg@329: * Called by the PLC, each time a python_eval etisserant@280: * FB instance is executed etisserant@280: */ etisserant@300: void __PythonEvalFB(int poll, PYTHON_EVAL* data__) etisserant@300: { greg@329: /* detect rising edge on TRIG to trigger evaluation */ greg@329: if(((data__->TRIG && !data__->TRIGM1) || etisserant@300: /* polling is equivalent to trig on value rather than on rising edge*/ etisserant@300: (poll && data__->TRIG )) && etisserant@300: /* trig only if not already trigged */ etisserant@300: 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@300: /* retain value for next rising edge 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@300: /* if some answer are waiting, publish*/ etisserant@300: if(data__->STATE == PYTHON_FB_ANSWERED){ etisserant@300: /* Copy buffer content into result*/ etisserant@300: data__->RESULT = data__->BUFFER; etisserant@300: /* signal result presece to PLC*/ etisserant@300: data__->ACK = 1; etisserant@300: /* Mark as free */ etisserant@300: data__->STATE = PYTHON_FB_FREE; etisserant@300: /* mark as not trigged */ etisserant@300: if(!poll) etisserant@300: data__->TRIGGED = 0; etisserant@300: /*printf("__PythonEvalFB pop %%d - %%*s\n",Current_PLC_EvalFB, data__->BUFFER.len, data__->BUFFER.body);*/ etisserant@300: }else if(poll){ etisserant@300: /* when in polling, no answer == ack down */ etisserant@300: data__->ACK = 0; etisserant@300: } etisserant@300: /* got the order to act ?*/ etisserant@281: if(data__->TRIGGED == 1 && greg@329: /* and not already being processed */ greg@329: 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 greg@329: * 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@300: if(!poll){ etisserant@300: /* when not polling, a new answer imply reseting ack*/ etisserant@300: data__->ACK = 0; etisserant@300: }else{ etisserant@300: /* when in polling, acting reset trigger */ etisserant@300: data__->TRIGGED = 0; etisserant@300: } 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@300: /*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: } 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 */ greg@329: while(((data__ = EvalFBs[Current_Python_EvalFB]) == NULL) || greg@329: /* 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"); greg@329: if(WaitPythonCommands()) return NULL; 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: