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 |
603 from threading import Timer, Lock, Thread |
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 |
664 |
665 |
665 # Setup debug information |
666 # Setup debug information |
666 self.IECdebug_datas = {} |
667 self.IECdebug_datas = {} |
667 self.IECdebug_lock = Lock() |
668 self.IECdebug_lock = Lock() |
668 |
669 |
669 # Timer to prevent rapid-fire when registering many variables |
670 self.DebugTimer=None |
670 self.DebugTimer=Timer(0.5,self.RegisterDebugVarToConnector) |
|
671 self.ResetIECProgramsAndVariables() |
671 self.ResetIECProgramsAndVariables() |
672 |
|
673 |
672 |
674 #This method are not called here... but in NewProject and OpenProject |
673 #This method are not called here... but in NewProject and OpenProject |
675 #self._AddParamsMembers() |
674 #self._AddParamsMembers() |
676 #self.PluggedChilds = {} |
675 #self.PluggedChilds = {} |
677 |
676 |
970 CSV file generated by IEC2C compiler. |
969 CSV file generated by IEC2C compiler. |
971 """ |
970 """ |
972 self._ProgramList = None |
971 self._ProgramList = None |
973 self._VariablesList = None |
972 self._VariablesList = None |
974 self._IECPathToIdx = None |
973 self._IECPathToIdx = None |
975 self._IdxToIECPath = None |
974 self.TracedIECPath = [] |
976 |
975 |
977 def GetIECProgramsAndVariables(self): |
976 def GetIECProgramsAndVariables(self): |
978 """ |
977 """ |
979 Parse CSV-like file VARIABLES.csv resulting from IEC2C compiler. |
978 Parse CSV-like file VARIABLES.csv resulting from IEC2C compiler. |
980 Each section is marked with a line staring with '//' |
979 Each section is marked with a line staring with '//' |
981 list of all variables used in various POUs |
980 list of all variables used in various POUs |
987 ProgramsListAttributeName = ["num", "C_path", "type"] |
986 ProgramsListAttributeName = ["num", "C_path", "type"] |
988 VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"] |
987 VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"] |
989 self._ProgramList = [] |
988 self._ProgramList = [] |
990 self._VariablesList = [] |
989 self._VariablesList = [] |
991 self._IECPathToIdx = {} |
990 self._IECPathToIdx = {} |
992 self._IdxToIECPath = {} |
|
993 |
991 |
994 # Separate sections |
992 # Separate sections |
995 ListGroup = [] |
993 ListGroup = [] |
996 for line in open(csvfile,'r').xreadlines(): |
994 for line in open(csvfile,'r').xreadlines(): |
997 strippedline = line.strip() |
995 strippedline = line.strip() |
1021 self._VariablesList.append(attrs) |
1019 self._VariablesList.append(attrs) |
1022 # Fill in IEC<->C translation dicts |
1020 # Fill in IEC<->C translation dicts |
1023 IEC_path=attrs["IEC_path"] |
1021 IEC_path=attrs["IEC_path"] |
1024 Idx=int(attrs["num"]) |
1022 Idx=int(attrs["num"]) |
1025 self._IECPathToIdx[IEC_path]=Idx |
1023 self._IECPathToIdx[IEC_path]=Idx |
1026 self._IdxToIECPath[Idx]=IEC_path |
|
1027 except Exception,e: |
1024 except Exception,e: |
1028 self.logger.write_error("Cannot open/parse VARIABLES.csv!\n") |
1025 self.logger.write_error("Cannot open/parse VARIABLES.csv!\n") |
1029 self.logger.write_error(traceback.format_exc()) |
1026 self.logger.write_error(traceback.format_exc()) |
1030 self.ResetIECProgramsAndVariables() |
1027 self.ResetIECProgramsAndVariables() |
1031 return False |
1028 return False |
1057 for v in self._VariablesList if v["type"] in DebugTypes ])} |
1054 for v in self._VariablesList if v["type"] in DebugTypes ])} |
1058 |
1055 |
1059 return debug_code |
1056 return debug_code |
1060 |
1057 |
1061 def RegisterDebugVarToConnector(self): |
1058 def RegisterDebugVarToConnector(self): |
|
1059 self.DebugTimer=None |
1062 Idxs = [] |
1060 Idxs = [] |
|
1061 self.TracedIECPath = [] |
1063 if self._connector is not None: |
1062 if self._connector is not None: |
1064 self.IECdebug_lock.acquire() |
1063 self.IECdebug_lock.acquire() |
1065 for IECPath,data_tuple in self.IECdebug_datas: |
1064 for IECPath,data_tuple in self.IECdebug_datas.iteritems(): |
1066 WeakCallableDict, data_log, status = data_tuple |
1065 WeakCallableDict, data_log, status = data_tuple |
1067 if len(WeakCallableDict) == 0: |
1066 if len(WeakCallableDict) == 0: |
1068 # Callable Dict is empty. |
1067 # Callable Dict is empty. |
1069 # This variable is not needed anymore! |
1068 # This variable is not needed anymore! |
1070 # self.IECdebug_callables.pop(IECPath) |
1069 # self.IECdebug_callables.pop(IECPath) |
1071 # TODO |
1070 # TODO |
1072 pass |
1071 print "Unused : " + IECPath |
1073 else: |
1072 else: |
1074 # Convert |
1073 # Convert |
1075 Idx = self._IECPathToIdx.get(IECPath,None) |
1074 Idx = self._IECPathToIdx.get(IECPath,None) |
1076 if Idx is not None: |
1075 if Idx is not None: |
1077 Idxs.append(Idx) |
1076 Idxs.append(Idx) |
|
1077 self.TracedIECPath.append(IECPath) |
1078 else: |
1078 else: |
1079 self.logger.write_warning("Debug : Unknown variable %s\n"%IECPath) |
1079 self.logger.write_warning("Debug : Unknown variable %s\n"%IECPath) |
|
1080 self._connector.SetTraceVariablesList(Idxs) |
1080 self.IECdebug_lock.release() |
1081 self.IECdebug_lock.release() |
1081 self._connector.TraceVariables(Idxs) |
1082 |
1082 |
1083 def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs): |
1083 def SubscribeDebugIECVariable(self, IECPath, callable, *args, **kwargs): |
|
1084 """ |
1084 """ |
1085 Dispatching use a dictionnary linking IEC variable paths |
1085 Dispatching use a dictionnary linking IEC variable paths |
1086 to a WeakKeyDictionary linking |
1086 to a WeakKeyDictionary linking |
1087 weakly referenced callables to optionnal args |
1087 weakly referenced callables to optionnal args |
1088 """ |
1088 """ |
1094 WeakKeyDictionary(), # Callables |
1094 WeakKeyDictionary(), # Callables |
1095 [], # Data storage [(tick, data),...] |
1095 [], # Data storage [(tick, data),...] |
1096 "Registered"] # Variable status |
1096 "Registered"] # Variable status |
1097 self.IECdebug_datas[IECPath] = IECdebug_data |
1097 self.IECdebug_datas[IECPath] = IECdebug_data |
1098 |
1098 |
1099 IECdebug_data[0][callable]=(args, kwargs) |
1099 IECdebug_data[0][callableobj]=(args, kwargs) |
1100 |
1100 |
1101 self.IECdebug_lock.release() |
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]) |
1102 # Rearm anti-rapid-fire timer |
1109 # Rearm anti-rapid-fire timer |
1103 self.DebugTimer.cancel() |
|
1104 self.DebugTimer.start() |
1110 self.DebugTimer.start() |
|
1111 |
|
1112 return IECdebug_data[1] |
1105 |
1113 |
1106 def Generate_plc_common_main(self): |
1114 def Generate_plc_common_main(self): |
1107 """ |
1115 """ |
1108 Use plugins layout given in LocationCFilesAndCFLAGS to |
1116 Use plugins layout given in LocationCFilesAndCFLAGS to |
1109 generate glue code that dispatch calls to all plugins |
1117 generate glue code that dispatch calls to all plugins |
1327 self.logger.write("Starting PLC\n") |
1335 self.logger.write("Starting PLC\n") |
1328 else: |
1336 else: |
1329 self.logger.write_error("Couldn't start PLC !\n") |
1337 self.logger.write_error("Couldn't start PLC !\n") |
1330 self.UpdateMethodsFromPLCStatus() |
1338 self.UpdateMethodsFromPLCStatus() |
1331 |
1339 |
1332 def _Debug(self): |
1340 def DebugThreadProc(self): |
|
1341 while self._connector is not None: |
|
1342 debug_tick, debug_vars = self._connector.GetTraceVariables() |
|
1343 print debug_tick, debug_vars |
|
1344 if debug_vars is not None and \ |
|
1345 len(debug_vars) == len(self.TracedIECPath): |
|
1346 for IECPath,value in zip(self.TracedIECPath, debug_vars): |
|
1347 data_tuple = self.IECdebug_datas.get(IECPath, None) |
|
1348 if data_tuple is not None: |
|
1349 WeakCallableDict, data_log, status = data_tuple |
|
1350 data_log.append((debug_tick, value)) |
|
1351 for weakcallable,(args,kwargs) in WeakCallableDict.iteritems(): |
|
1352 wx.CallAfter(weakcallable, value, *args, **kwargs) |
|
1353 elif debug_vars is not None: |
|
1354 wx.CallAfter(self.logger.write_warning, |
|
1355 "debug data not coherent %d != %d"%(len(debug_vars), len(self.TracedIECPath))) |
|
1356 elif debug_tick == -1: |
|
1357 #wx.CallAfter(self.logger.write, "Debugger unavailable\n") |
|
1358 pass |
|
1359 else: |
|
1360 wx.CallAfter(self.logger.write, "Debugger disabled\n") |
|
1361 break |
|
1362 |
|
1363 def _Debug(self): |
1333 """ |
1364 """ |
1334 Start PLC (Debug Mode) |
1365 Start PLC (Debug Mode) |
1335 """ |
1366 """ |
1336 if self.GetIECProgramsAndVariables() and self._connector.StartPLC(): |
1367 if self.GetIECProgramsAndVariables() and \ |
|
1368 self._connector.StartPLC(debug=True): |
1337 self.logger.write("Starting PLC (debug mode)\n") |
1369 self.logger.write("Starting PLC (debug mode)\n") |
1338 # TODO : laucnch PLCOpenEditor in Debug Mode |
1370 # TODO : laucnch PLCOpenEditor in Debug Mode |
1339 self.logger.write_warning("Debug mode for PLCopenEditor not implemented\n") |
1371 self.DebugThread = Thread(target=self.DebugThreadProc) |
1340 self.logger.write_warning("Starting alternative test GUI\n") |
1372 self.DebugThread.start() |
1341 # TODO : laucnch PLCOpenEditor in Debug Mode |
|
1342 else: |
1373 else: |
1343 self.logger.write_error("Couldn't start PLC debug !\n") |
1374 self.logger.write_error("Couldn't start PLC debug !\n") |
1344 self.UpdateMethodsFromPLCStatus() |
1375 self.UpdateMethodsFromPLCStatus() |
|
1376 |
|
1377 # def _Do_Test_Debug(self): |
|
1378 # # debug code |
|
1379 # self.temporary_non_weak_callable_refs = [] |
|
1380 # for IEC_Path, idx in self._IECPathToIdx.iteritems(): |
|
1381 # class tmpcls: |
|
1382 # def __init__(self): |
|
1383 # self.buf = None |
|
1384 # def setbuf(self,buf): |
|
1385 # self.buf = buf |
|
1386 # def __call__(self, value, idx, name): |
|
1387 # print "debug call:", value, idx, name, self.buf |
|
1388 # a = tmpcls() |
|
1389 # res = self.SubscribeDebugIECVariable(IEC_Path, a, idx, IEC_Path) |
|
1390 # a.setbuf(res) |
|
1391 # self.temporary_non_weak_callable_refs.append(a) |
1345 |
1392 |
1346 def _Stop(self): |
1393 def _Stop(self): |
1347 """ |
1394 """ |
1348 Stop PLC |
1395 Stop PLC |
1349 """ |
1396 """ |
1483 {"bitmap" : opjimg("Debug"), |
1530 {"bitmap" : opjimg("Debug"), |
1484 "name" : "Debug", |
1531 "name" : "Debug", |
1485 "shown" : False, |
1532 "shown" : False, |
1486 "tooltip" : "Start PLC (debug mode)", |
1533 "tooltip" : "Start PLC (debug mode)", |
1487 "method" : "_Debug"}, |
1534 "method" : "_Debug"}, |
|
1535 # {"bitmap" : opjimg("Debug"), |
|
1536 # "name" : "Do_Test_Debug", |
|
1537 # "tooltip" : "Test debug mode)", |
|
1538 # "method" : "_Do_Test_Debug"}, |
1488 {"bitmap" : opjimg("Stop"), |
1539 {"bitmap" : opjimg("Stop"), |
1489 "name" : "Stop", |
1540 "name" : "Stop", |
1490 "shown" : False, |
1541 "shown" : False, |
1491 "tooltip" : "Stop Running PLC", |
1542 "tooltip" : "Stop Running PLC", |
1492 "method" : "_Stop"}, |
1543 "method" : "_Stop"}, |