# HG changeset patch # User Edouard Tisserant # Date 1422611111 -3600 # Node ID 4a45f6642523c1781e74a0075b2523aeb5cc20a0 # Parent 8872223a675b41d0c5abc52c0e41e7f6c4f8802d Moved trace buffer unpacking in the IDE. Latest traced variable samples are now passed as a single string diff -r 8872223a675b -r 4a45f6642523 ProjectController.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: diff -r 8872223a675b -r 4a45f6642523 runtime/PLCObject.py --- 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, '', '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)) - - + + diff -r 8872223a675b -r 4a45f6642523 targets/typemapping.py --- 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