author | Edouard Tisserant <edouard@beremiz.fr> |
Thu, 16 Jan 2025 14:52:49 +0100 (2 months ago) | |
changeset 4096 | d459b9179ec3 |
parent 2272 | 28b0a783975e |
child 4104 | 9e59bb5ad9e1 |
permissions | -rw-r--r-- |
/* * 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 <string.h> /* 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_py_ext() { 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_py_ext() { PythonState = PYTHON_FINISHED; UnBlockPythonCommands(); } void __retrieve_py_ext() { /* 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_py_ext() { 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__) { if(!__GET_VAR(data__->TRIG)){ /* ACK is False when TRIG is false, except a pulse when receiving result */ __SET_VAR(data__->, ACK,, 0); } /* 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 presence 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, void** id) { 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, .len, strlen(result)); /* prevent results overrun */ if(__GET_VAR(data__->BUFFER, .len) > STR_MAX_LEN) { __SET_VAR(data__->, BUFFER, .len, STR_MAX_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, .len, 0); } /* 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, .body[__GET_VAR(data__->BUFFER, .len)], 0); /* next command is BUFFER */ next_command = (char*)__GET_VAR(data__->BUFFER, .body); *id=data__; /* free python mutex */ UnLockPython(); /* return the next command to eval */ return next_command; }