# HG changeset patch # User etisserant # Date 1230057464 -3600 # Node ID f2ef79f3dba0a93951be136207ce34d0027a911b # Parent 47d29c4b55a30331175d71b50b633a6f5862c407 Added native (not a plugin) asynchronous python eval function block - Beta. Code cleanup in C code templates. diff -r 47d29c4b55a3 -r f2ef79f3dba0 plugger.py --- a/plugger.py Tue Dec 23 19:31:28 2008 +0100 +++ b/plugger.py Tue Dec 23 19:37:44 2008 +0100 @@ -1078,6 +1078,23 @@ return debug_code + def Generate_plc_python(self): + """ + Generate trace/debug code out of PLC variable list + """ + self.GetIECProgramsAndVariables() + + python_eval_fb_list = [] + for v in self._VariablesList : + if v["vartype"] == "FB" and v["type"] == "PYTHON_EVAL": + python_eval_fb_list.append(v) + python_eval_fb_count = len(python_eval_fb_list) + + # prepare debug code + python_code = targets.code("plc_python") % { + "python_eval_fb_count": python_eval_fb_count} + return python_code + def Generate_plc_common_main(self): """ Use plugins layout given in LocationCFilesAndCFLAGS to @@ -1175,6 +1192,8 @@ for generator, filename, name in [ # debugger code (self.Generate_plc_debugger, "plc_debugger.c", "Debugger"), + # IEC<->python gateway code + (self.Generate_plc_python, "plc_python.c", "IEC-Python gateway"), # init/cleanup/retrieve/publish, run and align code (self.Generate_plc_common_main,"plc_common_main.c","Common runtime")]: try: @@ -1408,7 +1427,7 @@ # This will block thread if more than one call is waiting elif debug_vars is not None: wx.CallAfter(self.logger.write_warning, - "debug data not coherent %d != %d"%(len(debug_vars), len(self.TracedIECPath))) + "Debug data not coherent %d != %d\n"%(len(debug_vars), len(self.TracedIECPath))) elif debug_tick == -1: #wx.CallAfter(self.logger.write, "Debugger unavailable\n") pass diff -r 47d29c4b55a3 -r f2ef79f3dba0 pous.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pous.xml Tue Dec 23 19:37:44 2008 +0100 @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 47d29c4b55a3 -r f2ef79f3dba0 runtime/PLCObject.py --- a/runtime/PLCObject.py Tue Dec 23 19:31:28 2008 +0100 +++ b/runtime/PLCObject.py Tue Dec 23 19:37:44 2008 +0100 @@ -23,8 +23,9 @@ #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import Pyro.core as pyro -from threading import Timer +from threading import Timer, Thread import ctypes, os, commands +import time if os.name in ("nt", "ce"): from _ctypes import LoadLibrary as dlopen @@ -107,6 +108,10 @@ self._resumeDebug = self.PLClibraryHandle.resumeDebug self._resumeDebug.restype = None + + self._PythonIterator = self.PLClibraryHandle.PythonIterator + self._PythonIterator.restype = ctypes.c_char_p + self._PythonIterator.argtypes = [ctypes.c_char_p] return True except: @@ -128,6 +133,7 @@ self._WaitDebugData = lambda:-1 self._suspendDebug = lambda:None self._resumeDebug = lambda:None + self._PythonIterator = lambda:"" self.PLClibraryHandle = None # Unload library explicitely if getattr(self,"_PLClibraryHandle",None) is not None: @@ -163,6 +169,18 @@ return True return False + def PythonThreadProc(self): + res = "" + print "PythonThreadProc started" + while self.PLCStatus == "Started": + cmd = self._PythonIterator(res) + print "_PythonIterator(", res, ") -> ", cmd + try : + res = eval(cmd) + except Exception,e: + res = "#EXCEPTION : "+str(e) + print res + print "PythonThreadProc finished" def StartPLC(self, debug=False): print "StartPLC" @@ -174,6 +192,8 @@ self.PLCStatus = "Started" if self.statuschange is not None: self.statuschange(self.PLCStatus) + self.PythonThread = Thread(target=self.PythonThreadProc) + self.PythonThread.start() return True else: print "_StartPLC did not return 0 !" @@ -270,6 +290,13 @@ for idx in idxs: self._RegisterDebugVariable(idx) self._resumeDebug() + + class IEC_STRING(ctypes.Structure): + """ + Must be changed according to changes in iec_types.h + """ + _fields_ = [("len", ctypes.c_uint8), + ("body", ctypes.c_char * 40)] TypeTranslator = {"BOOL" : (ctypes.c_uint8, lambda x:x.value!=0), "STEP" : (ctypes.c_uint8, lambda x:x.value), @@ -278,7 +305,7 @@ "SINT" : (ctypes.c_int8, lambda x:x.value), "USINT" : (ctypes.c_uint8, lambda x:x.value), "BYTE" : (ctypes.c_uint8, lambda x:x.value), - "STRING" : (None, None),#TODO + "STRING" : (IEC_STRING, lambda x:x.body[:x.len]), "INT" : (ctypes.c_int16, lambda x:x.value), "UINT" : (ctypes.c_uint16, lambda x:x.value), "WORD" : (ctypes.c_uint16, lambda x:x.value), diff -r 47d29c4b55a3 -r f2ef79f3dba0 targets/Linux/plc_Linux_main.c --- a/targets/Linux/plc_Linux_main.c Tue Dec 23 19:31:28 2008 +0100 +++ b/targets/Linux/plc_Linux_main.c Tue Dec 23 19:37:44 2008 +0100 @@ -1,3 +1,7 @@ +/** + * Linux specific code + **/ + #include #include #include @@ -5,16 +9,14 @@ #include #include +/* provided by POUS.C */ +extern int common_ticktime__; + long AtomicCompareExchange(long* atomicvar,long compared, long exchange) { return __sync_val_compare_and_swap(atomicvar, compared, exchange); } -//long AtomicExchange(long* atomicvar,long exchange) -//{ -// return __sync_lock_test_and_set(atomicvar, exchange); -//} - void PLC_GetTime(IEC_TIME *CURRENT_TIME) { clock_gettime(CLOCK_REALTIME, CURRENT_TIME); @@ -64,9 +66,12 @@ static int __debug_tick; -static pthread_mutex_t wait_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t python_wait_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t python_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t debug_wait_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER; +#define maxval(a,b) ((a>b)?a:b) int startPLC(int argc,char **argv) { struct sigevent sigev; @@ -79,7 +84,8 @@ sigev.sigev_notify_attributes = NULL; sigev.sigev_notify_function = PLC_timer_notify; - pthread_mutex_lock(&wait_mutex); + pthread_mutex_lock(&debug_wait_mutex); + pthread_mutex_lock(&python_wait_mutex); timer_create (CLOCK_REALTIME, &sigev, &PLC_timer); if( __init(argc,argv) == 0 ){ @@ -111,7 +117,7 @@ timer_delete (PLC_timer); __cleanup(); __debug_tick = -1; - pthread_mutex_unlock(&wait_mutex); + pthread_mutex_unlock(&debug_wait_mutex); } extern int __tick; @@ -119,7 +125,7 @@ int WaitDebugData() { /* Wait signal from PLC thread */ - pthread_mutex_lock(&wait_mutex); + pthread_mutex_lock(&debug_wait_mutex); return __debug_tick; } @@ -127,25 +133,49 @@ * This is supposed to unlock debugger thread in WaitDebugData*/ void InitiateDebugTransfer() { - /* Leave debugger section */ - pthread_mutex_unlock(&debug_mutex); /* remember tick */ __debug_tick = __tick; /* signal debugger thread it can read data */ - pthread_mutex_unlock(&wait_mutex); + pthread_mutex_unlock(&debug_wait_mutex); } -void suspendDebug() +void suspendDebug(void) { - __DEBUG = 0; /* Prevent PLC to enter debug code */ pthread_mutex_lock(&debug_mutex); } -void resumeDebug() +void resumeDebug(void) { - __DEBUG = 1; /* Let PLC enter debug code */ pthread_mutex_unlock(&debug_mutex); } +/* from plc_python.c */ +int WaitPythonCommands(void) +{ + /* Wait signal from PLC thread */ + pthread_mutex_lock(&python_wait_mutex); +} + +/* Called by PLC thread on each new python command*/ +void UnBlockPythonCommands(void) +{ + /* signal debugger thread it can read data */ + pthread_mutex_unlock(&python_wait_mutex); +} + +int TryLockPython(void) +{ + return pthread_mutex_trylock(&python_mutex) == 0; +} + +void UnLockPython(void) +{ + pthread_mutex_unlock(&python_mutex); +} + +void LockPython(void) +{ + pthread_mutex_lock(&python_mutex); +} diff -r 47d29c4b55a3 -r f2ef79f3dba0 targets/Win32/plc_Win32_main.c --- a/targets/Win32/plc_Win32_main.c Tue Dec 23 19:31:28 2008 +0100 +++ b/targets/Win32/plc_Win32_main.c Tue Dec 23 19:37:44 2008 +0100 @@ -1,18 +1,20 @@ +/** + * Win32 specific code + **/ + #include #include #include #include +/* provided by POUS.C */ +extern int common_ticktime__; + long AtomicCompareExchange(long* atomicvar, long compared, long exchange) { return InterlockedCompareExchange(atomicvar, exchange, compared); } -//long AtomicExchange(long* atomicvar,long exchange) -//{ -// return InterlockedExchange(atomicvar, exchange); -//} - struct _timeb timetmp; void PLC_GetTime(IEC_TIME *CURRENT_TIME) { @@ -64,9 +66,11 @@ HANDLE PLC_thread; HANDLE debug_sem; -HANDLE wait_sem; -#define MAX_SEM_COUNT 1 - +HANDLE debug_wait_sem; +HANDLE python_sem; +HANDLE python_wait_sem; + +#define maxval(a,b) ((a>b)?a:b) int startPLC(int argc,char **argv) { unsigned long thread_id = 0; @@ -76,25 +80,50 @@ debug_sem = CreateSemaphore( NULL, // default security attributes 1, // initial count - MAX_SEM_COUNT, // maximum count + 1, // maximum count NULL); // unnamed semaphore if (debug_sem == NULL) { - printf("CreateMutex error: %d\n", GetLastError()); + printf("startPLC CreateSemaphore debug_sem error: %d\n", GetLastError()); return; } - wait_sem = CreateSemaphore( + debug_wait_sem = CreateSemaphore( NULL, // default security attributes 0, // initial count - MAX_SEM_COUNT, // maximum count - NULL); // unnamed semaphore - - if (wait_sem == NULL) - { - printf("CreateMutex error: %d\n", GetLastError()); - return; - } + 1, // maximum count + NULL); // unnamed semaphore + + if (debug_wait_sem == NULL) + { + printf("startPLC CreateSemaphore debug_wait_sem error: %d\n", GetLastError()); + return; + } + + python_sem = CreateSemaphore( + NULL, // default security attributes + 1, // initial count + 1, // maximum count + NULL); // unnamed semaphore + + if (python_sem == NULL) + { + printf("startPLC CreateSemaphore python_sem error: %d\n", GetLastError()); + return; + } + python_wait_sem = CreateSemaphore( + NULL, // default security attributes + 0, // initial count + 1, // maximum count + NULL); // unnamed semaphore + + + if (python_wait_sem == NULL) + { + printf("startPLC CreateSemaphore python_wait_sem error: %d\n", GetLastError()); + return; + } + /* Create a waitable timer */ PLC_timer = CreateWaitableTimer(NULL, FALSE, "WaitableTimer"); @@ -133,9 +162,9 @@ WaitForSingleObject(PLC_thread, INFINITE); __cleanup(); __debug_tick = -1; - ReleaseSemaphore(wait_sem, 1, NULL); + ReleaseSemaphore(debug_wait_sem, 1, NULL); CloseHandle(debug_sem); - CloseHandle(wait_sem); + CloseHandle(debug_wait_sem); CloseHandle(PLC_timer); CloseHandle(PLC_thread); } @@ -143,30 +172,58 @@ /* from plc_debugger.c */ int WaitDebugData() { - WaitForSingleObject(wait_sem, INFINITE); + WaitForSingleObject(debug_wait_sem, INFINITE); return __debug_tick; } -/* Called by PLC thread when debug_pu//blish finished +/* Called by PLC thread when debug_publish finished * This is supposed to unlock debugger thread in WaitDebugData*/ void InitiateDebugTransfer() { /* remember tick */ __debug_tick = __tick; /* signal debugger thread it can read data */ - ReleaseSemaphore(wait_sem, 1, NULL); + ReleaseSemaphore(debug_wait_sem, 1, NULL); } void suspendDebug() { - __DEBUG = 0; /* Prevent PLC to enter debug code */ WaitForSingleObject(debug_sem, INFINITE); } void resumeDebug() { - __DEBUG = 1; /* Let PLC enter debug code */ ReleaseSemaphore(debug_sem, 1, NULL); } + +/* from plc_python.c */ +int WaitPythonCommands(void) +{ + /* Wait signal from PLC thread */ + WaitForSingleObject(python_wait_sem, INFINITE); +} + +/* Called by PLC thread on each new python command*/ +void UnBlockPythonCommands(void) +{ + /* signal debugger thread it can read data */ + ReleaseSemaphore(python_wait_sem, 1, NULL); +} + +int TryLockPython(void) +{ + return WaitForSingleObject(python_sem, 0) == WAIT_OBJECT_0; +} + +void UnLockPython(void) +{ + ReleaseSemaphore(python_sem, 1, NULL); +} + +void LockPython(void) +{ + WaitForSingleObject(python_sem, INFINITE); +} + diff -r 47d29c4b55a3 -r f2ef79f3dba0 targets/plc_common_main.c --- a/targets/plc_common_main.c Tue Dec 23 19:31:28 2008 +0100 +++ b/targets/plc_common_main.c Tue Dec 23 19:37:44 2008 +0100 @@ -1,41 +1,35 @@ -/* - * Prototypes for function provided by arch-specific code (main) - * concatained after this template - ** / +/** + * Code common to all C targets + **/ +#include "iec_types.h" /* - * Functions and variables provied by generated C softPLC - **/ -extern int common_ticktime__; - -/* - * Functions and variables provied by plc.c - **/ -void run(long int tv_sec, long int tv_nsec); - -#define maxval(a,b) ((a>b)?a:b) - -#include "iec_types.h" -/*#include "stdio.h" /* For debug */ - -/* - * Functions and variables provied by generated C softPLC + * Prototypes of functions provied by generated C softPLC **/ void config_run__(int tick); void config_init__(void); + +/* + * Prototypes of functions provied by generated target C code + * */ void __init_debug(void); void __cleanup_debug(void); +/*void __retrieve_debug(void);*/ +void __publish_debug(void); +void __init_python(void); +void __cleanup_python(void); +void __retrieve_python(void); +void __publish_python(void); /* - * Functions and variables to export to generated C softPLC and plugins + * Variables used by generated C softPLC and plugins **/ - IEC_TIME __CURRENT_TIME; -IEC_BOOL __DEBUG; int __tick = -1; +/* Help to quit cleanly when init fail at a certain level */ static int init_level = 0; /* @@ -52,10 +46,14 @@ %(retrieve_calls)s + __retrieve_python(); + /*__retrieve_debug();*/ config_run__(__tick); + __publish_python(); + __publish_debug(); %(publish_calls)s @@ -169,3 +167,8 @@ } } } + +/** + * Prototypes for function provided by arch-specific code (main) + * is concatained hereafter + **/ \ No newline at end of file diff -r 47d29c4b55a3 -r f2ef79f3dba0 targets/plc_debug.c --- a/targets/plc_debug.c Tue Dec 23 19:31:28 2008 +0100 +++ b/targets/plc_debug.c Tue Dec 23 19:37:44 2008 +0100 @@ -53,10 +53,7 @@ void __init_debug() { %(variables_pointer_type_table_initializer)s -AtomicCompareExchange( - &buffer_state, - BUFFER_BUSY, - BUFFER_FREE); + buffer_state = BUFFER_FREE; } void __cleanup_debug() diff -r 47d29c4b55a3 -r f2ef79f3dba0 targets/plc_python.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/plc_python.c Tue Dec 23 19:37:44 2008 +0100 @@ -0,0 +1,179 @@ +/* + * 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 long Current_PLC_EvalFB; +static long 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 + +/* 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); + +void __init_python() +{ + 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; +} + +void __cleanup_python() +{ +} + +void __retrieve_python() +{ + /* 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_python() +{ + 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(PYTHON_EVAL* data__) +{ + /* python thread is not in ? */ + if( PythonState & PYTHON_LOCKED_BY_PLC){ + /* Rising edge on TRIG */ + if(data__->TRIG && !data__->TRIGM1 && + /* and not already being processed */ + data__->STATE == PYTHON_FB_FREE) + { + /* Get a new line */ + Current_PLC_EvalFB = (Current_PLC_EvalFB + 1) %% %(python_eval_fb_count)d; + /* 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 CODE in variable into BUFFER local*/ + data__->BUFFER = data__->CODE; + /* Set ACK pin to low so that we can set a rising edge on result */ + data__->ACK = 0; + /* Mark FB busy */ + data__->STATE = PYTHON_FB_REQUESTED; + /* Have to wakeup python thread in case he was asleep */ + PythonState |= PYTHON_MUSTWAKEUP; + //printf("__PythonEvalFB push %%*s\n",data__->BUFFER.len, data__->BUFFER.body); + }else if(data__->STATE == PYTHON_FB_ANSWERED){ + data__->RESULT = data__->BUFFER; + data__->ACK = 1; + data__->STATE = PYTHON_FB_FREE; + //printf("__PythonEvalFB pop %%*s\n",data__->BUFFER.len, data__->BUFFER.body); + } + /* retain value for trig + * do this only when PYTHON_LOCKED_BY_PLC + * to avoid missed rising edge due to asynchronism */ + data__->TRIGM1 = data__->TRIG; + } +} + +char* PythonIterator(char* result) +{ + char* next_command; + PYTHON_EVAL* data__; + //printf("PythonIterator result %%s\n", result); + /* 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 */ + data__->STATE == PYTHON_FB_PROCESSING){ /* some answer awaited*/ + /* If result not None */ + if(result){ + /* Get results len */ + data__->BUFFER.len = strlen(result); + /* prevent results overrun */ + if(data__->BUFFER.len > STR_MAX_LEN) + { + data__->BUFFER.len = STR_MAX_LEN; + /* TODO : signal error */ + } + /* Copy results to buffer */ + strncpy(data__->BUFFER.body, result, data__->BUFFER.len); + }else{ + data__->BUFFER.len = 0; + } + /* remove block from fifo*/ + EvalFBs[Current_Python_EvalFB] = NULL; + /* Mark block as answered */ + data__->STATE = PYTHON_FB_ANSWERED; + /* Get a new line */ + Current_Python_EvalFB = (Current_Python_EvalFB + 1) %% %(python_eval_fb_count)d; + } + /* while next slot is empty */ + while(((data__ = EvalFBs[Current_Python_EvalFB]) == NULL) || + /* or doesn't contain command */ + data__->STATE != PYTHON_FB_REQUESTED) + { + UnLockPython(); + /* wait next FB to eval */ + WaitPythonCommands(); + LockPython(); + } + /* Mark block as processing */ + data__->STATE = PYTHON_FB_PROCESSING; + //printf("PythonIterator\n"); + /* make BUFFER a null terminated string */ + data__->BUFFER.body[data__->BUFFER.len] = 0; + /* next command is BUFFER */ + next_command = data__->BUFFER.body; + /* free python mutex */ + UnLockPython(); + /* return the next command to eval */ + return next_command; +} + diff -r 47d29c4b55a3 -r f2ef79f3dba0 tests/python/beremiz.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/python/beremiz.xml Tue Dec 23 19:37:44 2008 +0100 @@ -0,0 +1,6 @@ + + + + + + diff -r 47d29c4b55a3 -r f2ef79f3dba0 tests/python/plc.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/python/plc.xml Tue Dec 23 19:37:44 2008 +0100 @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pytest_var3 + + + + + + + pytest_var1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pytest_var4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pytest_var2 + + + + + + + + + + + + + + + + +