plugger.py
changeset 239 112b4bc523b3
parent 236 a32817e81f5e
child 243 90db933fe956
equal deleted inserted replaced
238:02d0daed3e46 239:112b4bc523b3
   598 
   598 
   599 iec2c_path = os.path.join(base_folder, "matiec", "iec2c"+exe_ext)
   599 iec2c_path = os.path.join(base_folder, "matiec", "iec2c"+exe_ext)
   600 ieclib_path = os.path.join(base_folder, "matiec", "lib")
   600 ieclib_path = os.path.join(base_folder, "matiec", "lib")
   601 
   601 
   602 # import for project creation timestamping
   602 # import for project creation timestamping
   603 from threading import Timer, Lock, Thread
   603 from threading import Timer, Lock, Thread, Semaphore
   604 from time import localtime
   604 from time import localtime
   605 from datetime import datetime
   605 from datetime import datetime
   606 # import necessary stuff from PLCOpenEditor
   606 # import necessary stuff from PLCOpenEditor
   607 from PLCControler import PLCControler
   607 from PLCControler import PLCControler
   608 from PLCOpenEditor import PLCOpenEditor, ProjectDialog
   608 from PLCOpenEditor import PLCOpenEditor, ProjectDialog
  1052                 "VAR":"    variable_table[%(num)s].ptrvalue = (void*)(&%(C_path)s);\n"}[v["vartype"]]%v + 
  1052                 "VAR":"    variable_table[%(num)s].ptrvalue = (void*)(&%(C_path)s);\n"}[v["vartype"]]%v + 
  1053                 "    variable_table[%(num)s].type = %(type)s_ENUM;\n"%v
  1053                 "    variable_table[%(num)s].type = %(type)s_ENUM;\n"%v
  1054                 for v in self._VariablesList if v["type"] in DebugTypes ])}
  1054                 for v in self._VariablesList if v["type"] in DebugTypes ])}
  1055         
  1055         
  1056         return debug_code
  1056         return debug_code
  1057 
       
  1058     def RegisterDebugVarToConnector(self):
       
  1059         self.DebugTimer=None
       
  1060         Idxs = []
       
  1061         self.TracedIECPath = []
       
  1062         if self._connector is not None:
       
  1063             self.IECdebug_lock.acquire()
       
  1064             for IECPath,data_tuple in self.IECdebug_datas.iteritems():
       
  1065                 WeakCallableDict, data_log, status = data_tuple
       
  1066                 if len(WeakCallableDict) == 0:
       
  1067                     # Callable Dict is empty.
       
  1068                     # This variable is not needed anymore!
       
  1069                     # self.IECdebug_callables.pop(IECPath)
       
  1070                     # TODO
       
  1071                     print "Unused : " + IECPath
       
  1072                 else:
       
  1073                     # Convert 
       
  1074                     Idx = self._IECPathToIdx.get(IECPath,None)
       
  1075                     if Idx is not None:
       
  1076                         Idxs.append(Idx)
       
  1077                         self.TracedIECPath.append(IECPath)
       
  1078                     else:
       
  1079                         self.logger.write_warning("Debug : Unknown variable %s\n"%IECPath)
       
  1080             self._connector.SetTraceVariablesList(Idxs)
       
  1081             self.IECdebug_lock.release()
       
  1082         
       
  1083     def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs):
       
  1084         """
       
  1085         Dispatching use a dictionnary linking IEC variable paths
       
  1086         to a WeakKeyDictionary linking 
       
  1087         weakly referenced callables to optionnal args
       
  1088         """
       
  1089         self.IECdebug_lock.acquire()
       
  1090         # If no entry exist, create a new one with a fresh WeakKeyDictionary
       
  1091         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
       
  1092         if IECdebug_data is None:
       
  1093             IECdebug_data  = [
       
  1094                     WeakKeyDictionary(), # Callables
       
  1095                     [],                  # Data storage [(tick, data),...]
       
  1096                     "Registered"]        # Variable status
       
  1097             self.IECdebug_datas[IECPath] = IECdebug_data
       
  1098         
       
  1099         IECdebug_data[0][callableobj]=(args, kwargs)
       
  1100 
       
  1101         self.IECdebug_lock.release()
       
  1102 
       
  1103         if self.DebugTimer is not None:
       
  1104             self.DebugTimer.cancel()
       
  1105 
       
  1106         # Timer to prevent rapid-fire when registering many variables
       
  1107         # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead
       
  1108         self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector])
       
  1109         # Rearm anti-rapid-fire timer
       
  1110         self.DebugTimer.start()
       
  1111 
       
  1112         return IECdebug_data[1]
       
  1113         
  1057         
  1114     def Generate_plc_common_main(self):
  1058     def Generate_plc_common_main(self):
  1115         """
  1059         """
  1116         Use plugins layout given in LocationCFilesAndCFLAGS to
  1060         Use plugins layout given in LocationCFilesAndCFLAGS to
  1117         generate glue code that dispatch calls to all plugins
  1061         generate glue code that dispatch calls to all plugins
  1335             self.logger.write("Starting PLC\n")
  1279             self.logger.write("Starting PLC\n")
  1336         else:
  1280         else:
  1337             self.logger.write_error("Couldn't start PLC !\n")
  1281             self.logger.write_error("Couldn't start PLC !\n")
  1338         self.UpdateMethodsFromPLCStatus()
  1282         self.UpdateMethodsFromPLCStatus()
  1339 
  1283 
       
  1284     def RegisterDebugVarToConnector(self):
       
  1285         self.DebugTimer=None
       
  1286         Idxs = []
       
  1287         self.TracedIECPath = []
       
  1288         if self._connector is not None:
       
  1289             self.IECdebug_lock.acquire()
       
  1290             IECPathsToPop = []
       
  1291             for IECPath,data_tuple in self.IECdebug_datas.iteritems():
       
  1292                 WeakCallableDict, data_log, status = data_tuple
       
  1293                 if len(WeakCallableDict) == 0:
       
  1294                     # Callable Dict is empty.
       
  1295                     # This variable is not needed anymore!
       
  1296                     #print "Unused : " + IECPath
       
  1297                     IECPathsToPop.append(IECPath)
       
  1298                 else:
       
  1299                     # Convert 
       
  1300                     Idx = self._IECPathToIdx.get(IECPath,None)
       
  1301                     if Idx is not None:
       
  1302                         Idxs.append(Idx)
       
  1303                         self.TracedIECPath.append(IECPath)
       
  1304                     else:
       
  1305                         self.logger.write_warning("Debug : Unknown variable %s\n"%IECPath)
       
  1306             for IECPathToPop in IECPathsToPop:
       
  1307                 self.IECdebug_datas.pop(IECPathToPop)
       
  1308 
       
  1309             self._connector.SetTraceVariablesList(Idxs)
       
  1310             self.IECdebug_lock.release()
       
  1311         
       
  1312     def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs):
       
  1313         """
       
  1314         Dispatching use a dictionnary linking IEC variable paths
       
  1315         to a WeakKeyDictionary linking 
       
  1316         weakly referenced callables to optionnal args
       
  1317         """
       
  1318         self.IECdebug_lock.acquire()
       
  1319         # If no entry exist, create a new one with a fresh WeakKeyDictionary
       
  1320         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
       
  1321         if IECdebug_data is None:
       
  1322             IECdebug_data  = [
       
  1323                     WeakKeyDictionary(), # Callables
       
  1324                     [],                  # Data storage [(tick, data),...]
       
  1325                     "Registered"]        # Variable status
       
  1326             self.IECdebug_datas[IECPath] = IECdebug_data
       
  1327         
       
  1328         IECdebug_data[0][callableobj]=(args, kwargs)
       
  1329 
       
  1330         self.IECdebug_lock.release()
       
  1331 
       
  1332         if self.DebugTimer is not None:
       
  1333             self.DebugTimer.cancel()
       
  1334 
       
  1335         # Timer to prevent rapid-fire when registering many variables
       
  1336         # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead
       
  1337         self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector])
       
  1338         # Rearm anti-rapid-fire timer
       
  1339         self.DebugTimer.start()
       
  1340 
       
  1341         return IECdebug_data[1]
       
  1342 
       
  1343     def UnsubscribeDebugIECVariable(self, IECPath, callableobj):
       
  1344         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
       
  1345         if IECdebug_data is None:
       
  1346             IECdebug_data[0].pop(callableobj,None)
       
  1347 
       
  1348     def DebugCallerFunc(self, weakcallable, value, *args, **kwargs):
       
  1349         # do the call
       
  1350         weakcallable.SetValue(value, *args, **kwargs)
       
  1351         # will unlock debug thread
       
  1352         self.DebugThreadSlowDownLock.release()
       
  1353 
  1340     def DebugThreadProc(self):
  1354     def DebugThreadProc(self):
       
  1355         """
       
  1356         This thread waid PLC debug data, and dispatch them to subscribers
       
  1357         """
       
  1358         # This lock is used to avoid flooding wx event stack calling callafter
       
  1359         self.DebugThreadSlowDownLock = Semaphore(0)
  1341         while self._connector is not None:
  1360         while self._connector is not None:
  1342             debug_tick, debug_vars = self._connector.GetTraceVariables()
  1361             debug_tick, debug_vars = self._connector.GetTraceVariables()
  1343             print debug_tick, debug_vars
  1362             #print debug_tick, debug_vars
  1344             if debug_vars is not None and \
  1363             if debug_vars is not None and \
  1345                len(debug_vars) == len(self.TracedIECPath):
  1364                len(debug_vars) == len(self.TracedIECPath):
  1346                 for IECPath,value in zip(self.TracedIECPath, debug_vars):
  1365                 for IECPath,value in zip(self.TracedIECPath, debug_vars):
  1347                     data_tuple = self.IECdebug_datas.get(IECPath, None)
  1366                     data_tuple = self.IECdebug_datas.get(IECPath, None)
  1348                     if data_tuple is not None:
  1367                     if data_tuple is not None:
  1349                         WeakCallableDict, data_log, status = data_tuple
  1368                         WeakCallableDict, data_log, status = data_tuple
  1350                         data_log.append((debug_tick, value))
  1369                         data_log.append((debug_tick, value))
  1351                         for weakcallable,(args,kwargs) in WeakCallableDict.iteritems():
  1370                         for weakcallable,(args,kwargs) in WeakCallableDict.iteritems():
  1352                             wx.CallAfter(weakcallable.SetValue, value, *args, **kwargs)
  1371                             # delegate call to wx event loop
       
  1372                             wx.CallAfter(self.DebugCallerFunc, weakcallable, value, *args, **kwargs)
       
  1373                             # This will block thread if more than one call is waiting
       
  1374                             self.DebugThreadSlowDownLock.acquire()
  1353             elif debug_vars is not None:
  1375             elif debug_vars is not None:
  1354                 wx.CallAfter(self.logger.write_warning, 
  1376                 wx.CallAfter(self.logger.write_warning, 
  1355                              "debug data not coherent %d != %d"%(len(debug_vars), len(self.TracedIECPath)))
  1377                              "debug data not coherent %d != %d"%(len(debug_vars), len(self.TracedIECPath)))
  1356             elif debug_tick == -1:
  1378             #elif debug_tick == -1:
  1357                 #wx.CallAfter(self.logger.write, "Debugger unavailable\n")
  1379                 #wx.CallAfter(self.logger.write, "Debugger unavailable\n")
  1358                 pass
  1380             #    pass
  1359             else:
  1381             else:
  1360                 wx.CallAfter(self.logger.write, "Debugger disabled\n")
  1382                 wx.CallAfter(self.logger.write, "Debugger disabled\n")
  1361                 break
  1383                 break
  1362 
  1384 
  1363     def _Debug(self):
  1385     def _Debug(self):
  1365         Start PLC (Debug Mode)
  1387         Start PLC (Debug Mode)
  1366         """
  1388         """
  1367         if self.GetIECProgramsAndVariables() and \
  1389         if self.GetIECProgramsAndVariables() and \
  1368            self._connector.StartPLC(debug=True):
  1390            self._connector.StartPLC(debug=True):
  1369             self.logger.write("Starting PLC (debug mode)\n")
  1391             self.logger.write("Starting PLC (debug mode)\n")
       
  1392             self.TracedIECPath = []
  1370             # TODO : laucnch PLCOpenEditor in Debug Mode
  1393             # TODO : laucnch PLCOpenEditor in Debug Mode
  1371             self.DebugThread = Thread(target=self.DebugThreadProc)
  1394             self.DebugThread = Thread(target=self.DebugThreadProc)
  1372             self.DebugThread.start()
  1395             self.DebugThread.start()
  1373         else:
  1396         else:
  1374             self.logger.write_error("Couldn't start PLC debug !\n")
  1397             self.logger.write_error("Couldn't start PLC debug !\n")
  1377 #    def _Do_Test_Debug(self):
  1400 #    def _Do_Test_Debug(self):
  1378 #        # debug code
  1401 #        # debug code
  1379 #        self.temporary_non_weak_callable_refs = []
  1402 #        self.temporary_non_weak_callable_refs = []
  1380 #        for IEC_Path, idx in self._IECPathToIdx.iteritems():
  1403 #        for IEC_Path, idx in self._IECPathToIdx.iteritems():
  1381 #            class tmpcls:
  1404 #            class tmpcls:
  1382 #                def __init__(self):
  1405 #                def __init__(_self):
  1383 #                    self.buf = None
  1406 #                    self.buf = None
  1384 #                def setbuf(self,buf):
  1407 #                def setbuf(_self,buf):
  1385 #                    self.buf = buf
  1408 #                    self.buf = buf
  1386 #                def SetValue(self, value, idx, name):
  1409 #                def SetValue(_self, value, idx, name):
  1387 #                    print "debug call:", value, idx, name, self.buf
  1410 #                    self.logger.write("debug call: %s %d %s\n"%(repr(value), idx, name))
       
  1411 #                    #self.logger.write("debug call: %s %d %s %s\n"%(repr(value), idx, name, repr(self.buf)))
  1388 #            a = tmpcls()
  1412 #            a = tmpcls()
  1389 #            res = self.SubscribeDebugIECVariable(IEC_Path, a, idx, IEC_Path)
  1413 #            res = self.SubscribeDebugIECVariable(IEC_Path, a, idx, IEC_Path)
  1390 #            a.setbuf(res)
  1414 #            a.setbuf(res)
  1391 #            self.temporary_non_weak_callable_refs.append(a)
  1415 #            self.temporary_non_weak_callable_refs.append(a)
  1392        
  1416