Moved trace buffer unpacking in the IDE. Latest traced variable samples are now passed as a single string
--- 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