ProjectController.py
changeset 1475 de4ee16f7c6c
parent 1451 94e620cbd9de
child 1484 d5efbb20927e
--- a/ProjectController.py	Sat Dec 06 19:31:51 2014 +0000
+++ b/ProjectController.py	Wed Oct 21 15:00:32 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,15 +29,13 @@
 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]
+base_folder = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]
 
 MATIEC_ERROR_MODEL = re.compile(".*\.st:(\d+)-(\d+)\.\.(\d+)-(\d+): (?:error)|(?:warning) : (.*)$")
 
-DEBUG_RETRIES_WARN = 3
-DEBUG_RETRIES_REREGISTER = 4
-
 ITEM_CONFNODE = 25
 
 def ExtractChildrenTypesFromCatalog(catalog):
@@ -609,17 +608,21 @@
     def _Compile_ST_to_SoftPLC(self):
         self.logger.write(_("Compiling IEC Program into C code...\n"))
         buildpath = self._getBuildPath()
-
-        # Now compile IEC code into many C files
-        # files are listed to stdout, and errors to stderr.
-        status, result, err_result = ProcessLogger(
-               self.logger,
-               "\"%s\" -f -l -p -I \"%s\" -T \"%s\" \"%s\""%(
+        buildcmd = "\"%s\" -f -l -p -I \"%s\" -T \"%s\" \"%s\""%(
                          self.iec2c_path,
                          self.ieclib_path,
                          buildpath,
-                         self._getIECcodepath()),
-               no_stdout=True, no_stderr=True).spin()
+                         self._getIECcodepath())
+
+        try:
+            # Invoke compiler. Output files are listed to stdout, errors to stderr
+            status, result, err_result = ProcessLogger(self.logger, buildcmd,
+                no_stdout=True, no_stderr=True).spin()
+        except Exception,e:
+            self.logger.write_error(buildcmd + "\n")
+            self.logger.write_error(repr(e) + "\n")
+            return False
+
         if status:
             # Failed !
 
@@ -732,9 +735,11 @@
         """
         self._ProgramList = None
         self._VariablesList = None
+        self._DbgVariablesList = None
         self._IECPathToIdx = {}
         self._Ticktime = 0
         self.TracedIECPath = []
+        self.TracedIECTypes = []
 
     def GetIECProgramsAndVariables(self):
         """
@@ -750,6 +755,7 @@
                 VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"]
                 self._ProgramList = []
                 self._VariablesList = []
+                self._DbgVariablesList = []
                 self._IECPathToIdx = {}
 
                 # Separate sections
@@ -774,6 +780,7 @@
 
                 # second section contains all variables
                 config_FBs = {}
+                Idx = 0
                 for line in ListGroup[1]:
                     # Split and Maps each field to dictionnary entries
                     attrs = dict(zip(VariablesListAttributeName,line.strip().split(';')))
@@ -790,12 +797,17 @@
                         attrs["C_path"] = '__'.join(parts)
                         if attrs["vartype"] == "FB":
                             config_FBs[tuple(parts)] = attrs["C_path"]
-                    # Push this dictionnary into result.
+                    if attrs["vartype"] != "FB":
+                        # Push this dictionnary into result.
+                        self._DbgVariablesList.append(attrs)
+                        # Fill in IEC<->C translation dicts
+                        IEC_path=attrs["IEC_path"]
+                        self._IECPathToIdx[IEC_path]=(Idx, attrs["type"])
+                        # Ignores numbers given in CSV file
+                        # Idx=int(attrs["num"])
+                        # Count variables only, ignore FBs
+                        Idx+=1
                     self._VariablesList.append(attrs)
-                    # Fill in IEC<->C translation dicts
-                    IEC_path=attrs["IEC_path"]
-                    Idx=int(attrs["num"])
-                    self._IECPathToIdx[IEC_path]=(Idx, attrs["type"])
 
                 # third section contains ticktime
                 if len(ListGroup) > 2:
@@ -816,8 +828,21 @@
         self.GetIECProgramsAndVariables()
 
         # prepare debug code
-        debug_code = targets.GetCode("plc_debug") % {
-           "buffer_size": reduce(lambda x, y: x + y, [DebugTypesSize.get(v["type"], 0) for v in self._VariablesList], 0),
+        variable_decl_array = []
+        bofs = 0
+        for v in self._DbgVariablesList :
+            sz = DebugTypesSize.get(v["type"], 0)
+            variable_decl_array += [
+                "{&(%(C_path)s), "%v+
+                {"EXT":"%(type)s_P_ENUM",
+                 "IN":"%(type)s_P_ENUM",
+                 "MEM":"%(type)s_O_ENUM",
+                 "OUT":"%(type)s_O_ENUM",
+                 "VAR":"%(type)s_ENUM"}[v["vartype"]]%v +
+                 "}"]
+            bofs += sz
+        debug_code = targets.GetCode("plc_debug.c") % {
+           "buffer_size":bofs,
            "programs_declarations":
                "\n".join(["extern %(type)s %(C_path)s;"%p for p in self._ProgramList]),
            "extern_variables_declarations":"\n".join([
@@ -828,22 +853,8 @@
                "VAR":"extern __IEC_%(type)s_t %(C_path)s;",
                "FB":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v
                for v in self._VariablesList if v["C_path"].find('.')<0]),
-           "for_each_variable_do_code":"\n".join([
-               {"EXT":"    (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n",
-                "IN":"    (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n",
-                "MEM":"    (*fp)((void*)&(%(C_path)s),%(type)s_O_ENUM);\n",
-                "OUT":"    (*fp)((void*)&(%(C_path)s),%(type)s_O_ENUM);\n",
-                "VAR":"    (*fp)((void*)&(%(C_path)s),%(type)s_ENUM);\n"}[v["vartype"]]%v
-                for v in self._VariablesList if v["vartype"] != "FB" and v["type"] in DebugTypesSize ]),
-           "find_variable_case_code":"\n".join([
-               "    case %(num)s:\n"%v+
-               "        *varp = (void*)&(%(C_path)s);\n"%v+
-               {"EXT":"        return %(type)s_P_ENUM;\n",
-                "IN":"        return %(type)s_P_ENUM;\n",
-                "MEM":"        return %(type)s_O_ENUM;\n",
-                "OUT":"        return %(type)s_O_ENUM;\n",
-                "VAR":"        return %(type)s_ENUM;\n"}[v["vartype"]]%v
-                for v in self._VariablesList if v["vartype"] != "FB" and v["type"] in DebugTypesSize ])}
+           "variable_decl_array": ",\n".join(variable_decl_array)
+           }
 
         return debug_code
 
@@ -859,7 +870,7 @@
 
         # Generate main, based on template
         if not self.BeremizRoot.getDisable_Extensions():
-            plc_main_code = targets.GetCode("plc_main_head") % {
+            plc_main_code = targets.GetCode("plc_main_head.c") % {
                 "calls_prototypes":"\n".join([(
                       "int __init_%(s)s(int argc,char **argv);\n"+
                       "void __cleanup_%(s)s(void);\n"+
@@ -879,7 +890,7 @@
                       "__cleanup_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)])
                 }
         else:
-            plc_main_code = targets.GetCode("plc_main_head") % {
+            plc_main_code = targets.GetCode("plc_main_head.c") % {
                 "calls_prototypes":"\n",
                 "retrieve_calls":"\n",
                 "publish_calls":"\n",
@@ -887,7 +898,7 @@
                 "cleanup_calls":"\n"
                 }
         plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent().getLocalTag())
-        plc_main_code += targets.GetCode("plc_main_tail")
+        plc_main_code += targets.GetCode("plc_main_tail.c")
         return plc_main_code
 
 
@@ -975,7 +986,7 @@
             self.ResetBuildMD5()
             return False
 
-        self.LocationCFilesAndCFLAGS =  CTNLocationCFilesAndCFLAGS + LibCFilesAndCFLAGS
+        self.LocationCFilesAndCFLAGS =  LibCFilesAndCFLAGS + CTNLocationCFilesAndCFLAGS
         self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS
         ExtraFiles = CTNExtraFiles + LibExtraFiles
 
@@ -1206,7 +1217,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
 
@@ -1214,6 +1225,7 @@
         self.DebugTimer=None
         Idxs = []
         self.TracedIECPath = []
+        self.TracedIECTypes = []
         if self._connector is not None:
             self.IECdebug_lock.acquire()
             IECPathsToPop = []
@@ -1238,8 +1250,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([])
@@ -1267,11 +1281,11 @@
         Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None))
         return IEC_Type
 
-    def SubscribeDebugIECVariable(self, IECPath, callableobj, buffer_list=False, *args, **kwargs):
+    def SubscribeDebugIECVariable(self, IECPath, callableobj, buffer_list=False):
         """
         Dispatching use a dictionnary linking IEC variable paths
         to a WeakKeyDictionary linking
-        weakly referenced callables to optionnal args
+        weakly referenced callables
         """
         if IECPath != "__tick__" and not self._IECPathToIdx.has_key(IECPath):
             return None
@@ -1290,7 +1304,7 @@
         else:
             IECdebug_data[4] |= buffer_list
 
-        IECdebug_data[0][callableobj]=(buffer_list, args, kwargs)
+        IECdebug_data[0][callableobj]=buffer_list
 
         self.IECdebug_lock.release()
 
@@ -1308,8 +1322,7 @@
             else:
                 IECdebug_data[4] = reduce(
                     lambda x, y: x|y,
-                    [buffer_list for buffer_list,args,kwargs
-                     in IECdebug_data[0].itervalues()],
+                    IECdebug_data[0].itervalues(),
                     False)
         self.IECdebug_lock.release()
 
@@ -1357,13 +1370,13 @@
         if data_tuple is not None:
             WeakCallableDict, data_log, status, fvalue, buffer_list = data_tuple
             #data_log.append((debug_tick, value))
-            for weakcallable,(buffer_list,args,kwargs) in WeakCallableDict.iteritems():
+            for weakcallable,buffer_list in WeakCallableDict.iteritems():
                 function = getattr(weakcallable, function_name, None)
                 if function is not None:
                     if buffer_list:
-                        function(*(cargs + args), **kwargs)
+                        function(*cargs)
                     else:
-                        function(*(tuple([lst[-1] for lst in cargs]) + args), **kwargs)
+                        function(*tuple([lst[-1] for lst in cargs]))
 
     def GetTicktime(self):
         return self._Ticktime
@@ -1380,38 +1393,34 @@
         self.debug_break = False
         debug_getvar_retry = 0
         while (not self.debug_break) and (self._connector is not None):
-            Trace = self._connector.GetTraceVariables()
-            if(Trace):
-                plc_status, debug_tick, debug_vars = Trace
-            else:
-                plc_status = None
+            plc_status, Traces = self._connector.GetTraceVariables()
             debug_getvar_retry += 1
             #print [dict.keys() for IECPath, (dict, log, status, fvalue) in self.IECdebug_datas.items()]
-            if plc_status == "Started":
-                self.IECdebug_lock.acquire()
-                if (debug_tick is not None and
-                    len(debug_vars) == len(self.DebugValuesBuffers) 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)
-                        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:
-                                values_buffer[-1] = (value, forced)
-                            else:
-                                values_buffer.append((value, forced))
-                    self.DebugTicks.append(debug_tick)
-                self.IECdebug_lock.release()
-                if debug_getvar_retry == DEBUG_RETRIES_WARN:
-                    self.logger.write(_("Waiting debugger to recover...\n"))
-                if debug_getvar_retry == DEBUG_RETRIES_REREGISTER:
-                    # re-register debug registry to PLC
-                    wx.CallAfter(self.RegisterDebugVarToConnector)
+            if plc_status == "Started" :
+                if len(Traces) > 0:
+                    Failed = False
+                    self.IECdebug_lock.acquire()
+                    for debug_tick, debug_buff in Traces :
+                        debug_vars = UnpackDebugBuffer(debug_buff, self.TracedIECTypes)
+                        if (debug_vars is not None and
+                            len(debug_vars) == len(self.TracedIECPath)):
+                            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:
+                                        values_buffer[-1] = (value, forced)
+                                    else:
+                                        values_buffer.append((value, forced))
+                            self.DebugTicks.append(debug_tick)
+                            debug_getvar_retry = 0
+                    self.IECdebug_lock.release()
+
                 if debug_getvar_retry != 0:
-                    # Be patient, tollerate PLC to come up before debugging
+                    # Be patient, tollerate PLC to come with fresh samples
                     time.sleep(0.1)
             else:
                 self.debug_break = True
@@ -1426,7 +1435,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: