Moved trace buffer unpacking in the IDE. Latest traced variable samples are now passed as a single string
authorEdouard Tisserant
Fri, 30 Jan 2015 10:45:11 +0100
changeset 1433 4a45f6642523
parent 1432 8872223a675b
child 1434 6e0cd0ceabb7
Moved trace buffer unpacking in the IDE. Latest traced variable samples are now passed as a single string
ProjectController.py
runtime/PLCObject.py
targets/typemapping.py
--- a/ProjectController.py	Thu Jan 29 19:11:34 2015 +0100
+++ b/ProjectController.py	Fri Jan 30 10:45:11 2015 +0100
@@ -13,6 +13,7 @@
 from time import localtime
 from datetime import datetime
 from weakref import WeakKeyDictionary
+from itertools import izip
 
 import targets
 import connectors
@@ -28,6 +29,7 @@
 from PLCControler import PLCControler
 from plcopen.structures import IEC_KEYWORDS
 from targets.typemapping import DebugTypesSize, LogLevelsCount, LogLevels
+from targets.typemapping import UnpackDebugBuffer
 from ConfigTreeNode import ConfigTreeNode, XSDSchemaErrorMessage
 
 base_folder = os.path.split(sys.path[0])[0]
@@ -736,6 +738,7 @@
         self._IECPathToIdx = {}
         self._Ticktime = 0
         self.TracedIECPath = []
+        self.TracedIECTypes = []
 
     def GetIECProgramsAndVariables(self):
         """
@@ -1213,7 +1216,7 @@
 
     def SnapshotAndResetDebugValuesBuffers(self):
         buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers,
-            [list() for iec_path in self.TracedIECPath])
+            [list() for n in xrange(len(self.TracedIECPath))])
         ticks, self.DebugTicks = self.DebugTicks, []
         return ticks, buffers
 
@@ -1221,6 +1224,7 @@
         self.DebugTimer=None
         Idxs = []
         self.TracedIECPath = []
+        self.TracedIECTypes = []
         if self._connector is not None:
             self.IECdebug_lock.acquire()
             IECPathsToPop = []
@@ -1245,8 +1249,10 @@
 
             if Idxs:
                 Idxs.sort()
-                self.TracedIECPath = zip(*Idxs)[3]
-                self._connector.SetTraceVariablesList(zip(*zip(*Idxs)[0:3]))
+                IdxsT = zip(*Idxs)
+                self.TracedIECPath = IdxsT[3]
+                self.TracedIECTypes = IdxsT[1]
+                self._connector.SetTraceVariablesList(zip(*IdxsT[0:3]))
             else:
                 self.TracedIECPath = []
                 self._connector.SetTraceVariablesList([])
@@ -1388,21 +1394,24 @@
         while (not self.debug_break) and (self._connector is not None):
             Trace = self._connector.GetTraceVariables()
             if(Trace):
-                plc_status, debug_tick, debug_vars = Trace
+                plc_status, debug_tick, debug_buff = Trace
             else:
                 plc_status = None
             debug_getvar_retry += 1
             #print [dict.keys() for IECPath, (dict, log, status, fvalue) in self.IECdebug_datas.items()]
-            if plc_status == "Started":
+            if plc_status == "Started" and debug_buff is not None:
                 self.IECdebug_lock.acquire()
-                if (debug_tick is not None and
-                    len(debug_vars) == len(self.DebugValuesBuffers) and
+                debug_vars = UnpackDebugBuffer(debug_buff, self.TracedIECTypes)
+                if (debug_tick is not None and debug_vars is not None and
                     len(debug_vars) == len(self.TracedIECPath)):
                     if debug_getvar_retry > DEBUG_RETRIES_WARN:
                         self.logger.write(_("... debugger recovered\n"))
                     debug_getvar_retry = 0
-                    for IECPath, values_buffer, value in zip(self.TracedIECPath, self.DebugValuesBuffers, debug_vars):
-                        IECdebug_data = self.IECdebug_datas.get(IECPath, None)
+                    for IECPath, values_buffer, value in izip(
+                            self.TracedIECPath,
+                            self.DebugValuesBuffers,
+                            debug_vars):
+                        IECdebug_data = self.IECdebug_datas.get(IECPath, None) #FIXME get
                         if IECdebug_data is not None and value is not None:
                             forced = IECdebug_data[2:4] == ["Forced", value]
                             if not IECdebug_data[4] and len(values_buffer) > 0:
@@ -1432,7 +1441,7 @@
         self.IECdebug_lock.release()
         start_time = time.time()
         if len(self.TracedIECPath) == len(buffers):
-            for IECPath, values in zip(self.TracedIECPath, buffers):
+            for IECPath, values in izip(self.TracedIECPath, buffers):
                 if len(values) > 0:
                     self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values)
             if len(debug_ticks) > 0:
--- a/runtime/PLCObject.py	Thu Jan 29 19:11:34 2015 +0100
+++ b/runtime/PLCObject.py	Fri Jan 30 10:45:11 2015 +0100
@@ -2,7 +2,7 @@
 # -*- coding: utf-8 -*-
 
 #This file is part of Beremiz, a Integrated Development Environment for
-#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. 
+#programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
 #
 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
 #
@@ -68,7 +68,7 @@
         self.website = website
         self._loading_error = None
         self.python_runtime_vars = None
-        
+
         # Get the last transfered PLC if connector must be restart
         try:
             self.CurrentPLCFilename=open(
@@ -107,7 +107,7 @@
         tv_nsec = ctypes.c_uint32()
         if self._GetLogMessage is not None:
             maxsz = len(self._log_read_buffer)-1
-            sz = self._GetLogMessage(level, msgid, 
+            sz = self._GetLogMessage(level, msgid,
                 self._log_read_buffer, maxsz,
                 ctypes.byref(tick),
                 ctypes.byref(tv_sec),
@@ -134,23 +134,23 @@
         try:
             self._PLClibraryHandle = dlopen(self._GetLibFileName())
             self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle)
-    
+
             self._startPLC = self.PLClibraryHandle.startPLC
             self._startPLC.restype = ctypes.c_int
             self._startPLC.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p)]
-            
+
             self._stopPLC_real = self.PLClibraryHandle.stopPLC
             self._stopPLC_real.restype = None
-            
+
             self._PythonIterator = getattr(self.PLClibraryHandle, "PythonIterator", None)
             if self._PythonIterator is not None:
                 self._PythonIterator.restype = ctypes.c_char_p
                 self._PythonIterator.argtypes = [ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)]
-                
+
                 self._stopPLC = self._stopPLC_real
             else:
                 # If python confnode is not enabled, we reuse _PythonIterator
-                # as a call that block pythonthread until StopPLC 
+                # as a call that block pythonthread until StopPLC
                 self.PythonIteratorLock = Lock()
                 self.PythonIteratorLock.acquire()
                 def PythonIterator(res, blkid):
@@ -158,25 +158,25 @@
                     self.PythonIteratorLock.release()
                     return None
                 self._PythonIterator = PythonIterator
-                
+
                 def __StopPLC():
                     self._stopPLC_real()
                     self.PythonIteratorLock.release()
                 self._stopPLC = __StopPLC
-            
-    
+
+
             self._ResetDebugVariables = self.PLClibraryHandle.ResetDebugVariables
             self._ResetDebugVariables.restype = None
-    
+
             self._RegisterDebugVariable = self.PLClibraryHandle.RegisterDebugVariable
             self._RegisterDebugVariable.restype = None
             self._RegisterDebugVariable.argtypes = [ctypes.c_int, ctypes.c_void_p]
-    
+
             self._FreeDebugData = self.PLClibraryHandle.FreeDebugData
             self._FreeDebugData.restype = None
-            
+
             self._GetDebugData = self.PLClibraryHandle.GetDebugData
-            self._GetDebugData.restype = ctypes.c_int  
+            self._GetDebugData.restype = ctypes.c_int
             self._GetDebugData.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_void_p)]
 
             self._suspendDebug = self.PLClibraryHandle.suspendDebug
@@ -233,7 +233,7 @@
         self._suspendDebug = lambda x:-1
         self._resumeDebug = lambda:None
         self._PythonIterator = lambda:""
-        self._GetLogCount = None 
+        self._GetLogCount = None
         self._LogMessage = lambda l,m,s:PLCprint("OFF LOG :"+m)
         self._GetLogMessage = None
         self.PLClibraryHandle = None
@@ -241,18 +241,18 @@
         if getattr(self,"_PLClibraryHandle",None) is not None:
             dlclose(self._PLClibraryHandle)
             self._PLClibraryHandle = None
-        
+
         self.PLClibraryLock.release()
         return False
 
     def PythonRuntimeCall(self, methodname):
-        """ 
-        Calls init, start, stop or cleanup method provided by 
+        """
+        Calls init, start, stop or cleanup method provided by
         runtime python files, loaded when new PLC uploaded
         """
         for method in self.python_runtime_vars.get("_runtime_%s"%methodname, []):
             res,exp = self.evaluator(method)
-            if exp is not None: 
+            if exp is not None:
                 self.LogMessage(0,'\n'.join(traceback.format_exception(*exp)))
 
     def PythonRuntimeInit(self):
@@ -286,14 +286,14 @@
                 name, ext = os.path.splitext(filename)
                 if name.upper().startswith("RUNTIME") and ext.upper() == ".PY":
                     execfile(os.path.join(self.workingdir, filename), self.python_runtime_vars)
-                    for methodname in MethodNames: 
+                    for methodname in MethodNames:
                         method = self.python_runtime_vars.get("_%s_%s" % (name, methodname), None)
                         if method is not None:
                             self.python_runtime_vars["_runtime_%s"%methodname].append(method)
         except:
             self.LogMessage(0,traceback.format_exc())
             raise
-            
+
         self.PythonRuntimeCall("init")
 
         if self.website is not None:
@@ -319,7 +319,7 @@
         while True:
             # print "_PythonIterator(", res, ")",
             cmd = self._PythonIterator(res,blkid)
-            FBID = blkid.value 
+            FBID = blkid.value
             # print " -> ", cmd, blkid
             if cmd is None:
                 break
@@ -330,7 +330,7 @@
                     AST = compile(cmd, '<plc>', 'eval')
                     compile_cache[FBID]=(cmd,AST)
                 result,exp = self.evaluator(eval,AST,self.python_runtime_vars)
-                if exp is not None: 
+                if exp is not None:
                     res = "#EXCEPTION : "+str(exp[1])
                     self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"')%(FBID,cmd,
                         '\n'.join(traceback.format_exception(*exp))))
@@ -343,7 +343,7 @@
         self.PLCStatus = "Stopped"
         self.StatusChange()
         self.PythonRuntimeCall("stop")
-    
+
     def StartPLC(self):
         if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped":
             c_argv = ctypes.c_char_p * len(self.argv)
@@ -359,7 +359,7 @@
                 self.LogMessage(0,_("Problem starting PLC : error %d" % res))
                 self.PLCStatus = "Broken"
                 self.StatusChange()
-            
+
     def StopPLC(self):
         if self.PLCStatus == "Started":
             self.LogMessage("PLC stopped")
@@ -382,7 +382,7 @@
 
     def GetPLCstatus(self):
         return self.PLCStatus, map(self.GetLogCount,xrange(LogLevelsCount))
-    
+
     def NewPLC(self, md5sum, data, extrafiles):
         if self.PLCStatus in ["Stopped", "Empty", "Broken"]:
             NewFileName = md5sum + lib_ext
@@ -403,15 +403,15 @@
                         pass
             except:
                 pass
-                        
+
             try:
                 # Create new PLC file
                 open(os.path.join(self.workingdir,NewFileName),
                      'wb').write(data)
-        
+
                 # Store new PLC filename based on md5 key
                 open(self._GetMD5FileName(), "w").write(md5sum)
-        
+
                 # Then write the files
                 log = file(extra_files_log, "w")
                 for fname,fdata in extrafiles:
@@ -443,12 +443,10 @@
             return last_md5 == MD5
         except:
             return False
-    
-
-    
+
     def SetTraceVariablesList(self, idxs):
         """
-        Call ctype imported function to append 
+        Call ctype imported function to append
         these indexes to registred variables in PLC debugger
         """
         if idxs:
@@ -462,7 +460,7 @@
                         c_type,unpack_func, pack_func = \
                             TypeTranslator.get(iectype,
                                                     (None,None,None))
-                        force = ctypes.byref(pack_func(c_type,force)) 
+                        force = ctypes.byref(pack_func(c_type,force))
                     self._RegisterDebugVariable(idx, force)
                 self._resumeDebug()
         else:
@@ -477,18 +475,18 @@
             tick = ctypes.c_uint32()
             size = ctypes.c_uint32()
             buff = ctypes.c_void_p()
-            TraceVariables = None
+            TraceBuffer = None
             if self.PLClibraryLock.acquire(False):
                 if self._GetDebugData(ctypes.byref(tick),
                                       ctypes.byref(size),
                                       ctypes.byref(buff)) == 0:
                     if size.value:
-                        TraceVariables = UnpackDebugBuffer(buff, size.value, self._Idxs)
+                        TraceBuffer = ctypes.string_at(buff.value, size.value)
                     self._FreeDebugData()
                 self.PLClibraryLock.release()
-            if TraceVariables is not None:
-                return self.PLCStatus, tick.value, TraceVariables
-        return self.PLCStatus, None, []
+            if TraceBuffer is not None:
+                return self.PLCStatus, tick.value, TraceBuffer
+        return self.PLCStatus, None, None
 
     def RemoteExec(self, script, **kwargs):
         try:
@@ -496,8 +494,8 @@
         except:
             e_type, e_value, e_traceback = sys.exc_info()
             line_no = traceback.tb_lineno(get_last_traceback(e_traceback))
-            return (-1, "RemoteExec script failed!\n\nLine %d: %s\n\t%s" % 
+            return (-1, "RemoteExec script failed!\n\nLine %d: %s\n\t%s" %
                         (line_no, e_value, script.splitlines()[line_no - 1]))
         return (0, kwargs.get("returnVal", None))
-    
-        
+
+
--- a/targets/typemapping.py	Thu Jan 29 19:11:34 2015 +0100
+++ b/targets/typemapping.py	Fri Jan 30 10:45:11 2015 +0100
@@ -19,6 +19,11 @@
 #License along with this library; if not, write to the Free Software
 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+import ctypes
+ctypes.pythonapi.PyString_AsString.argtypes = (ctypes.c_void_p,)
+ctypes.pythonapi.PyString_AsString.restype = ctypes.POINTER(ctypes.c_char)
+
+
 from ctypes import *
 from datetime import timedelta as td
 
@@ -27,7 +32,7 @@
     Must be changed according to changes in iec_types.h
     """
     _fields_ = [("len", c_uint8),
-                ("body", c_char * 126)] 
+                ("body", c_char * 126)]
 
 class IEC_TIME(Structure):
     """
@@ -37,8 +42,8 @@
                 ("ns", c_long)] #tv_nsec
 
 def _t(t, u=lambda x:x.value, p=lambda t,x:t(x)): return  (t, u, p)
-def _ttime(): return (IEC_TIME, 
-                      lambda x:td(0, x.s, x.ns/1000), 
+def _ttime(): return (IEC_TIME,
+                      lambda x:td(0, x.s, x.ns/1000),
                       lambda t,x:t(x.days * 24 * 3600 + x.seconds, x.microseconds*1000))
 
 SameEndianessTypeTranslator = {
@@ -49,8 +54,8 @@
     "SINT" :       _t(c_int8),
     "USINT" :      _t(c_uint8),
     "BYTE" :       _t(c_uint8),
-    "STRING" :     (IEC_STRING, 
-                      lambda x:x.body[:x.len], 
+    "STRING" :     (IEC_STRING,
+                      lambda x:x.body[:x.len],
                       lambda t,x:t(len(x),x)),
     "INT" :        _t(c_int16),
     "UINT" :       _t(c_uint16),
@@ -67,38 +72,35 @@
     "TOD" :        _ttime(),
     "DATE" :       _ttime(),
     "DT" :         _ttime(),
-    } 
+    }
 
 SwapedEndianessTypeTranslator = {
     #TODO
-    } 
+    }
 
 TypeTranslator=SameEndianessTypeTranslator
 
 # Construct debugger natively supported types
 DebugTypesSize =  dict([(key,sizeof(t)) for key,(t,p,u) in SameEndianessTypeTranslator.iteritems() if t is not None])
 
-def UnpackDebugBuffer(buff, size, indexes):
-    res = []
-    offset = 0
-    for idx, iectype, forced in indexes:
-        cursor = c_void_p(buff.value + offset)
+def UnpackDebugBuffer(buff, indexes):
+    res =  []
+    buffoffset = 0
+    buffsize = len(buff)
+    buffptr = cast(ctypes.pythonapi.PyString_AsString(id(buff)),c_void_p).value
+    for iectype in indexes:
         c_type,unpack_func, pack_func = \
             TypeTranslator.get(iectype,
                                     (None,None,None))
-        if c_type is not None and offset < size:
-            res.append(unpack_func(
-                        cast(cursor,
-                         POINTER(c_type)).contents))
-            offset += sizeof(c_type) if iectype != "STRING" else len(res[-1])+1
+        if c_type is not None and buffoffset < buffsize:
+            cursor = c_void_p( buffptr + buffoffset)
+            value = unpack_func( cast(cursor,
+                         POINTER(c_type)).contents)
+            buffoffset += sizeof(c_type) if iectype != "STRING" else len(value)+1
+            res.append(value)
         else:
-            #if c_type is None:
-            #    PLCprint("Debug error - " + iectype +
-            #             " not supported !")
-            #if offset >= size:
-            #    PLCprint("Debug error - buffer too small ! %d != %d"%(offset, size))
             break
-    if offset and offset == size:
+    if buffoffset and buffoffset == buffsize:
         return res
     return None