Added native (not a plugin) asynchronous python eval function block - Beta. Code cleanup in C code templates.
authoretisserant
Tue, 23 Dec 2008 19:37:44 +0100
changeset 280 f2ef79f3dba0
parent 279 47d29c4b55a3
child 281 3f0fc8de99b0
Added native (not a plugin) asynchronous python eval function block - Beta. Code cleanup in C code templates.
plugger.py
pous.xml
runtime/PLCObject.py
targets/Linux/plc_Linux_main.c
targets/Win32/plc_Win32_main.c
targets/plc_common_main.c
targets/plc_debug.c
targets/plc_python.c
tests/python/beremiz.xml
tests/python/plc.xml
--- 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
--- /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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://www.plcopen.org/xml/tc6.xsd"
+         xmlns:xhtml="http://www.w3.org/1999/xhtml"
+         xsi:schemaLocation="http://www.plcopen.org/xml/tc6.xsd">
+  <fileHeader companyName="LOLITECH"
+              productName="Beremiz"
+              productVersion="0.0"
+              creationDateTime="2008-12-14T16:53:26"/>
+  <contentHeader name="Beremiz non-standard POUs library"
+                 modificationDateTime="2008-12-18T18:41:34">
+    <coordinateInfo>
+      <fbd>
+        <scaling x="0" y="0"/>
+      </fbd>
+      <ld>
+        <scaling x="0" y="0"/>
+      </ld>
+      <sfc>
+        <scaling x="0" y="0"/>
+      </sfc>
+    </coordinateInfo>
+  </contentHeader>
+  <types>
+    <dataTypes/>
+    <pous>
+      <pou name="python_eval" pouType="functionBlock">
+        <interface>
+          <inputVars>
+            <variable name="TRIG">
+              <type>
+                <BOOL/>
+              </type>
+            </variable>
+            <variable name="CODE">
+              <type>
+                <string/>
+              </type>
+            </variable>
+          </inputVars>
+          <outputVars>
+            <variable name="ACK">
+              <type>
+                <BOOL/>
+              </type>
+            </variable>
+            <variable name="RESULT">
+              <type>
+                <string/>
+              </type>
+            </variable>
+          </outputVars>
+          <localVars>
+            <variable name="STATE">
+              <type>
+                <DWORD/>
+              </type>
+            </variable>
+            <variable name="BUFFER">
+              <type>
+                <string/>
+              </type>
+            </variable>
+            <variable name="TRIGM1">
+              <type>
+                <BOOL/>
+              </type>
+            </variable>
+          </localVars>
+        </interface>
+        <body>
+          <ST>
+<![CDATA[{extern void __PythonEvalFB(PYTHON_EVAL*);__PythonEvalFB(data__);}]]>
+          </ST>
+        </body>
+      </pou>
+    </pous>
+  </types>
+  <instances>
+    <configurations/>
+  </instances>
+</project>
--- 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),
--- 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 <stdio.h>
 #include <string.h>
 #include <time.h>
@@ -5,16 +9,14 @@
 #include <stdlib.h>
 #include <pthread.h> 
 
+/* 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);
+}
--- 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 <stdio.h>
 #include <sys/timeb.h>
 #include <time.h>
 #include <windows.h>
 
+/* 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);
+}
+
--- 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
--- 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()
--- /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 <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 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;
+}
+
--- /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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<BeremizRoot URI_location="PYRO://127.0.0.1:3000">
+  <TargetType>
+    <Linux CFLAGS="-g" LDFLAGS="-g"/>
+  </TargetType>
+</BeremizRoot>
--- /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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://www.plcopen.org/xml/tc6.xsd"
+         xmlns:xhtml="http://www.w3.org/1999/xhtml"
+         xsi:schemaLocation="http://www.plcopen.org/xml/tc6.xsd">
+  <fileHeader companyName="LOLITECH"
+              productName="Beremiz"
+              productVersion="0.0"
+              creationDateTime="2008-12-14T16:21:19"/>
+  <contentHeader name="Beremiz Python Support Tests"
+                 modificationDateTime="2008-12-22T23:58:02">
+    <coordinateInfo>
+      <pageSize x="1024" y="1024"/>
+      <fbd>
+        <scaling x="5" y="5"/>
+      </fbd>
+      <ld>
+        <scaling x="5" y="5"/>
+      </ld>
+      <sfc>
+        <scaling x="5" y="5"/>
+      </sfc>
+    </coordinateInfo>
+  </contentHeader>
+  <types>
+    <dataTypes/>
+    <pous>
+      <pou name="main_pytest" pouType="program">
+        <interface>
+          <localVars>
+            <variable name="pytest_var1">
+              <type>
+                <string/>
+              </type>
+              <initialValue>
+                <simpleValue value="time.sleep(1)"/>
+              </initialValue>
+            </variable>
+            <variable name="pytest_var2">
+              <type>
+                <BOOL/>
+              </type>
+            </variable>
+            <variable name="pytest_var3">
+              <type>
+                <string/>
+              </type>
+            </variable>
+            <variable name="pytest_var4">
+              <type>
+                <BOOL/>
+              </type>
+            </variable>
+            <variable name="py1">
+              <type>
+                <derived name="python_eval"/>
+              </type>
+            </variable>
+          </localVars>
+        </interface>
+        <body>
+          <FBD>
+            <outVariable localId="2" height="30" width="120">
+              <position x="760" y="150"/>
+              <connectionPointIn>
+                <relPosition x="0" y="15"/>
+                <connection refLocalId="5" formalParameter="RESULT">
+                  <position x="760" y="165"/>
+                  <position x="515" y="165"/>
+                </connection>
+              </connectionPointIn>
+              <expression>pytest_var3</expression>
+            </outVariable>
+            <inVariable localId="4" height="30" width="120">
+              <position x="35" y="150"/>
+              <connectionPointOut>
+                <relPosition x="120" y="15"/>
+              </connectionPointOut>
+              <expression>pytest_var1</expression>
+            </inVariable>
+            <block localId="5" width="125" height="80" typeName="python_eval" instanceName="py1">
+              <position x="390" y="100"/>
+              <inputVariables>
+                <variable formalParameter="TRIG">
+                  <connectionPointIn>
+                    <relPosition x="0" y="35"/>
+                    <connection refLocalId="7" formalParameter="OUT">
+                      <position x="390" y="135"/>
+                      <position x="190" y="135"/>
+                      <position x="190" y="55"/>
+                      <position x="130" y="55"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="CODE">
+                  <connectionPointIn>
+                    <relPosition x="0" y="65"/>
+                    <connection refLocalId="4">
+                      <position x="390" y="165"/>
+                      <position x="155" y="165"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+              </inputVariables>
+              <inOutVariables/>
+              <outputVariables>
+                <variable formalParameter="ACK">
+                  <connectionPointOut>
+                    <relPosition x="125" y="35"/>
+                  </connectionPointOut>
+                </variable>
+                <variable formalParameter="RESULT">
+                  <connectionPointOut>
+                    <relPosition x="125" y="65"/>
+                  </connectionPointOut>
+                </variable>
+              </outputVariables>
+            </block>
+            <outVariable localId="6" height="30" width="120">
+              <position x="760" y="120"/>
+              <connectionPointIn>
+                <relPosition x="0" y="15"/>
+                <connection refLocalId="5" formalParameter="ACK">
+                  <position x="760" y="135"/>
+                  <position x="515" y="135"/>
+                </connection>
+              </connectionPointIn>
+              <expression>pytest_var4</expression>
+            </outVariable>
+            <block localId="7" width="70" height="45" typeName="NOT">
+              <position x="60" y="25"/>
+              <inputVariables>
+                <variable formalParameter="IN">
+                  <connectionPointIn>
+                    <relPosition x="0" y="30"/>
+                    <connection refLocalId="3">
+                      <position x="60" y="55"/>
+                      <position x="45" y="55"/>
+                      <position x="45" y="85"/>
+                      <position x="165" y="85"/>
+                      <position x="165" y="120"/>
+                      <position x="155" y="120"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+              </inputVariables>
+              <inOutVariables/>
+              <outputVariables>
+                <variable formalParameter="OUT">
+                  <connectionPointOut>
+                    <relPosition x="70" y="30"/>
+                  </connectionPointOut>
+                </variable>
+              </outputVariables>
+            </block>
+            <inOutVariable localId="3" height="30" width="120">
+              <position x="35" y="105"/>
+              <connectionPointIn>
+                <relPosition x="0" y="15"/>
+                <connection refLocalId="7" formalParameter="OUT">
+                  <position x="35" y="120"/>
+                  <position x="25" y="120"/>
+                  <position x="25" y="15"/>
+                  <position x="210" y="15"/>
+                  <position x="210" y="55"/>
+                  <position x="130" y="55"/>
+                </connection>
+              </connectionPointIn>
+              <connectionPointOut>
+                <relPosition x="120" y="15"/>
+              </connectionPointOut>
+              <expression>pytest_var2</expression>
+            </inOutVariable>
+          </FBD>
+        </body>
+      </pou>
+    </pous>
+  </types>
+  <instances>
+    <configurations>
+      <configuration name="conf_pytest">
+        <resource name="res_pytest">
+          <task name="pytest_task" interval="00:00:00.100000" priority="0"/>
+          <pouInstance name="pytest_instance" type="main_pytest"/>
+        </resource>
+      </configuration>
+    </configurations>
+  </instances>
+</project>