681 # Keep track of the plugin type name |
681 # Keep track of the plugin type name |
682 self.PlugType = "Beremiz" |
682 self.PlugType = "Beremiz" |
683 # After __init__ root plugin is not valid |
683 # After __init__ root plugin is not valid |
684 self.ProjectPath = None |
684 self.ProjectPath = None |
685 self.PLCEditor = None |
685 self.PLCEditor = None |
|
686 self.PLCDebug = None |
686 # copy PluginMethods so that it can be later customized |
687 # copy PluginMethods so that it can be later customized |
687 self.PluginMethods = [dic.copy() for dic in self.PluginMethods] |
688 self.PluginMethods = [dic.copy() for dic in self.PluginMethods] |
688 |
689 |
689 def PlugTestModified(self): |
690 def PlugTestModified(self): |
690 return self.ChangesToSave or not self.ProjectIsSaved() |
691 return self.ChangesToSave or not self.ProjectIsSaved() |
1306 for IECPathToPop in IECPathsToPop: |
1307 for IECPathToPop in IECPathsToPop: |
1307 self.IECdebug_datas.pop(IECPathToPop) |
1308 self.IECdebug_datas.pop(IECPathToPop) |
1308 |
1309 |
1309 self._connector.SetTraceVariablesList(Idxs) |
1310 self._connector.SetTraceVariablesList(Idxs) |
1310 self.IECdebug_lock.release() |
1311 self.IECdebug_lock.release() |
|
1312 |
|
1313 #for IEC_path, IECdebug_data in self.IECdebug_datas.iteritems(): |
|
1314 # print IEC_path, IECdebug_data[0].keys() |
|
1315 |
|
1316 def ReArmDebugRegisterTimer(self): |
|
1317 if self.DebugTimer is not None: |
|
1318 self.DebugTimer.cancel() |
|
1319 |
|
1320 # Timer to prevent rapid-fire when registering many variables |
|
1321 # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead |
|
1322 self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector]) |
|
1323 # Rearm anti-rapid-fire timer |
|
1324 self.DebugTimer.start() |
|
1325 |
1311 |
1326 |
1312 def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs): |
1327 def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs): |
1313 """ |
1328 """ |
1314 Dispatching use a dictionnary linking IEC variable paths |
1329 Dispatching use a dictionnary linking IEC variable paths |
1315 to a WeakKeyDictionary linking |
1330 to a WeakKeyDictionary linking |
1326 self.IECdebug_datas[IECPath] = IECdebug_data |
1341 self.IECdebug_datas[IECPath] = IECdebug_data |
1327 |
1342 |
1328 IECdebug_data[0][callableobj]=(args, kwargs) |
1343 IECdebug_data[0][callableobj]=(args, kwargs) |
1329 |
1344 |
1330 self.IECdebug_lock.release() |
1345 self.IECdebug_lock.release() |
1331 |
1346 |
1332 if self.DebugTimer is not None: |
1347 self.ReArmDebugRegisterTimer() |
1333 self.DebugTimer.cancel() |
1348 |
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] |
1349 return IECdebug_data[1] |
1342 |
1350 |
1343 def UnsubscribeDebugIECVariable(self, IECPath, callableobj): |
1351 def UnsubscribeDebugIECVariable(self, IECPath, callableobj): |
|
1352 #print "Unsubscribe", IECPath, callableobj |
|
1353 self.IECdebug_lock.acquire() |
1344 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1354 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1345 if IECdebug_data is None: |
1355 if IECdebug_data is not None: |
1346 IECdebug_data[0].pop(callableobj,None) |
1356 IECdebug_data[0].pop(callableobj,None) |
|
1357 self.IECdebug_lock.release() |
|
1358 |
|
1359 self.ReArmDebugRegisterTimer() |
1347 |
1360 |
1348 def DebugCallerFunc(self, weakcallable, value, *args, **kwargs): |
1361 def DebugCallerFunc(self, weakcallable, value, *args, **kwargs): |
1349 # do the call |
1362 # do the call |
1350 weakcallable.SetValue(value, *args, **kwargs) |
1363 weakcallable.SetValue(value, *args, **kwargs) |
1351 # will unlock debug thread |
1364 # will unlock debug thread |
1358 # This lock is used to avoid flooding wx event stack calling callafter |
1371 # This lock is used to avoid flooding wx event stack calling callafter |
1359 self.DebugThreadSlowDownLock = Semaphore(0) |
1372 self.DebugThreadSlowDownLock = Semaphore(0) |
1360 while self._connector is not None: |
1373 while self._connector is not None: |
1361 debug_tick, debug_vars = self._connector.GetTraceVariables() |
1374 debug_tick, debug_vars = self._connector.GetTraceVariables() |
1362 #print debug_tick, debug_vars |
1375 #print debug_tick, debug_vars |
|
1376 self.IECdebug_lock.acquire() |
1363 if debug_vars is not None and \ |
1377 if debug_vars is not None and \ |
1364 len(debug_vars) == len(self.TracedIECPath): |
1378 len(debug_vars) == len(self.TracedIECPath): |
1365 for IECPath,value in zip(self.TracedIECPath, debug_vars): |
1379 for IECPath,value in zip(self.TracedIECPath, debug_vars): |
1366 data_tuple = self.IECdebug_datas.get(IECPath, None) |
1380 data_tuple = self.IECdebug_datas.get(IECPath, None) |
1367 if data_tuple is not None: |
1381 if data_tuple is not None: |
1373 # This will block thread if more than one call is waiting |
1387 # This will block thread if more than one call is waiting |
1374 self.DebugThreadSlowDownLock.acquire() |
1388 self.DebugThreadSlowDownLock.acquire() |
1375 elif debug_vars is not None: |
1389 elif debug_vars is not None: |
1376 wx.CallAfter(self.logger.write_warning, |
1390 wx.CallAfter(self.logger.write_warning, |
1377 "debug data not coherent %d != %d"%(len(debug_vars), len(self.TracedIECPath))) |
1391 "debug data not coherent %d != %d"%(len(debug_vars), len(self.TracedIECPath))) |
1378 #elif debug_tick == -1: |
1392 elif debug_tick == -1: |
1379 #wx.CallAfter(self.logger.write, "Debugger unavailable\n") |
1393 #wx.CallAfter(self.logger.write, "Debugger unavailable\n") |
1380 # pass |
1394 pass |
1381 else: |
1395 else: |
1382 wx.CallAfter(self.logger.write, "Debugger disabled\n") |
1396 wx.CallAfter(self.logger.write, "Debugger disabled\n") |
1383 break |
1397 break |
|
1398 self.IECdebug_lock.release() |
1384 |
1399 |
1385 def _Debug(self): |
1400 def _Debug(self): |
1386 """ |
1401 """ |
1387 Start PLC (Debug Mode) |
1402 Start PLC (Debug Mode) |
1388 """ |
1403 """ |
1389 if self.GetIECProgramsAndVariables() and \ |
1404 if self.GetIECProgramsAndVariables() and \ |
1390 self._connector.StartPLC(debug=True): |
1405 self._connector.StartPLC(debug=True): |
1391 self.logger.write("Starting PLC (debug mode)\n") |
1406 self.logger.write("Starting PLC (debug mode)\n") |
1392 self.TracedIECPath = [] |
1407 self.TracedIECPath = [] |
1393 # TODO : laucnch PLCOpenEditor in Debug Mode |
1408 if self.PLCDebug is None: |
|
1409 self.RefreshPluginsBlockLists() |
|
1410 def _onclose(): |
|
1411 self.PLCDebug = None |
|
1412 self.PLCDebug = PLCOpenEditor(self.AppFrame, self, debug=True) |
|
1413 self.PLCDebug._onclose = _onclose |
|
1414 self.PLCDebug.Show() |
1394 self.DebugThread = Thread(target=self.DebugThreadProc) |
1415 self.DebugThread = Thread(target=self.DebugThreadProc) |
1395 self.DebugThread.start() |
1416 self.DebugThread.start() |
1396 else: |
1417 else: |
1397 self.logger.write_error("Couldn't start PLC debug !\n") |
1418 self.logger.write_error("Couldn't start PLC debug !\n") |
1398 self.UpdateMethodsFromPLCStatus() |
1419 self.UpdateMethodsFromPLCStatus() |