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): |
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 |