24 |
24 |
25 __version__ = "$Revision$" |
25 __version__ = "$Revision$" |
26 |
26 |
27 import wx |
27 import wx |
28 |
28 |
29 from time import localtime |
|
30 from datetime import datetime |
|
31 import types |
29 import types |
32 |
30 |
33 import os, re, platform, sys, time, traceback, getopt, commands |
31 import os, re, platform, sys, time, traceback, getopt, commands |
34 base_folder = os.path.split(sys.path[0])[0] |
32 |
35 sys.path.append(os.path.join(base_folder, "plcopeneditor")) |
|
36 sys.path.append(os.path.join(base_folder, "CanFestival-3", "objdictgen")) |
|
37 sys.path.append(os.path.join(base_folder, "wxsvg", "svgui", "defeditor")) |
|
38 |
|
39 iec2cc_path = os.path.join(base_folder, "matiec", "iec2cc") |
|
40 ieclib_path = os.path.join(base_folder, "matiec", "lib") |
|
41 |
|
42 from PLCOpenEditor import PLCOpenEditor, ProjectDialog |
|
43 from TextViewer import TextViewer |
|
44 from plcopen.structures import IEC_KEYWORDS#, AddPlugin |
|
45 from plugger import PluginsRoot |
33 from plugger import PluginsRoot |
46 |
34 |
47 class LogPseudoFile: |
35 class LogPseudoFile: |
48 """ Base class for file like objects to facilitate StdOut for the Shell.""" |
36 """ Base class for file like objects to facilitate StdOut for the Shell.""" |
49 def __init__(self, output = None): |
37 def __init__(self, output = None): |
77 def flush(self): |
65 def flush(self): |
78 self.output.SetValue("") |
66 self.output.SetValue("") |
79 |
67 |
80 def isatty(self): |
68 def isatty(self): |
81 return false |
69 return false |
|
70 |
|
71 def LogCommand(self, Command, sz_limit = 100): |
|
72 |
|
73 import os, popen2, fcntl, select, signal |
|
74 |
|
75 child = popen2.Popen3(Command, 1) # capture stdout and stderr from command |
|
76 child.tochild.close() # don't need to talk to child |
|
77 outfile = child.fromchild |
|
78 outfd = outfile.fileno() |
|
79 errfile = child.childerr |
|
80 errfd = errfile.fileno() |
|
81 outdata = errdata = '' |
|
82 outeof = erreof = 0 |
|
83 outlen = errlen = 0 |
|
84 while 1: |
|
85 ready = select.select([outfd,errfd],[],[]) # wait for input |
|
86 if outfd in ready[0]: |
|
87 outchunk = outfile.readline() |
|
88 if outchunk == '': outeof = 1 |
|
89 else : outlen += 1 |
|
90 outdata += outchunk |
|
91 self.write(outchunk) |
|
92 if errfd in ready[0]: |
|
93 errchunk = errfile.readline() |
|
94 if errchunk == '': erreof = 1 |
|
95 else : errlen += 1 |
|
96 errdata += errchunk |
|
97 self.write_warning(errchunk) |
|
98 if outeof and erreof : break |
|
99 if errlen > sz_limit or outlen > sz_limit : |
|
100 os.kill(child.pid, signal.SIGTERM) |
|
101 self.write_error("Output size reached limit -- killed\n") |
|
102 break |
|
103 err = child.wait() |
|
104 return (err, outdata, errdata) |
82 |
105 |
83 [ID_BEREMIZ, ID_BEREMIZMAINSPLITTER, |
106 [ID_BEREMIZ, ID_BEREMIZMAINSPLITTER, |
84 ID_BEREMIZSECONDSPLITTER, ID_BEREMIZLEFTPANEL, |
107 ID_BEREMIZSECONDSPLITTER, ID_BEREMIZLEFTPANEL, |
85 ID_BEREMIZPARAMSPANEL, ID_BEREMIZLOGCONSOLE, |
108 ID_BEREMIZPARAMSPANEL, ID_BEREMIZLOGCONSOLE, |
86 ID_BEREMIZPLUGINTREE, ID_BEREMIZPLUGINCHILDS, |
109 ID_BEREMIZPLUGINTREE, ID_BEREMIZPLUGINCHILDS, |
586 boxsizer.AddWindow(textctrl, 0, border=0, flag=0) |
607 boxsizer.AddWindow(textctrl, 0, border=0, flag=0) |
587 textctrl.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(textctrl, element_path), id=id) |
608 textctrl.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(textctrl, element_path), id=id) |
588 textctrl.SetValue(str(element_infos["value"])) |
609 textctrl.SetValue(str(element_infos["value"])) |
589 first = False |
610 first = False |
590 |
611 |
591 def UpdateAttributesTreeParts(self, tree, new_tree): |
|
592 tree_leafs = [(element_infos["name"], element_infos["type"]) for element_infos in tree["children"]] |
|
593 new_tree_leafs = [(element_infos["name"], element_infos["type"]) for element_infos in new_tree["children"]] |
|
594 if tree_leafs != new_tree_leafs: |
|
595 tree["children"] = new_tree["children"] |
|
596 for child in tree["children"]: |
|
597 self.PrepareAttributesTree(child) |
|
598 else: |
|
599 for idx, new_element_infos in enumerate(new_tree["children"]): |
|
600 tree["children"][idx]["value"] = new_element_infos["value"] |
|
601 if len(new_element_infos["children"]) > 0: |
|
602 self.UpdateAttributesTreeParts(tree["children"][idx], new_element_infos) |
|
603 |
|
604 def PrepareAttributesTree(self, tree): |
|
605 if len(tree["children"]) > 0: |
|
606 tree["open"] = False |
|
607 for child in tree["children"]: |
|
608 self.PrepareAttributesTree(child) |
|
609 |
|
610 def GenerateTable(self, data, tree, path, indent): |
|
611 if path: |
|
612 tree_path = "%s.%s"%(path, tree["name"]) |
|
613 infos = {"Attribute" : " " * indent + tree["name"], "Value" : tree["value"], "Type" : tree["type"], "Open" : "", "Path" : tree_path} |
|
614 data.append(infos) |
|
615 indent += 1 |
|
616 else: |
|
617 tree_path = tree["name"] |
|
618 if len(tree["children"]) > 0: |
|
619 if tree["open"] or not path: |
|
620 if path: |
|
621 infos["Open"] = "v" |
|
622 for child in tree["children"]: |
|
623 self.GenerateTable(data, child, tree_path, indent) |
|
624 elif path: |
|
625 infos["Open"] = ">" |
|
626 |
|
627 def RefreshAttributesGrid(self): |
|
628 plugin = self.GetSelectedPlugin() |
|
629 if not plugin: |
|
630 self.AttributesTree = [] |
|
631 self.Table.Empty() |
|
632 else: |
|
633 new_params = plugin.GetParamsAttributes() |
|
634 for idx, child in enumerate(new_params): |
|
635 if len(self.AttributesTree) > idx: |
|
636 if self.AttributesTree[idx]["name"] == child["name"]: |
|
637 self.UpdateAttributesTreeParts(self.AttributesTree[idx], child) |
|
638 else: |
|
639 self.AttributesTree[idx] = child |
|
640 self.PrepareAttributesTree(child) |
|
641 else: |
|
642 self.AttributesTree.append(child) |
|
643 self.PrepareAttributesTree(child) |
|
644 while len(self.AttributesTree) > len(new_params): |
|
645 self.AttributesTree.pop(-1) |
|
646 data = [] |
|
647 for child in self.AttributesTree: |
|
648 self.GenerateTable(data, child, None, 0) |
|
649 self.Table.SetData(data) |
|
650 self.Table.ResetView(self.AttributesGrid) |
|
651 |
|
652 def OpenClose(self, tree, path): |
|
653 parts = path.split(".", 1) |
|
654 for child in tree["children"]: |
|
655 if child["name"] == parts[0]: |
|
656 if len(parts) > 1: |
|
657 return self.OpenClose(child, parts[1]) |
|
658 elif len(child["children"]) > 0: |
|
659 child["open"] = not child["open"] |
|
660 return True |
|
661 return False |
|
662 |
|
663 def OpenCloseAttribute(self): |
|
664 if self.AttributesGrid.GetGridCursorCol() == 0: |
|
665 row = self.AttributesGrid.GetGridCursorRow() |
|
666 path = self.Table.GetValueByName(row, "Path") |
|
667 parts = path.split(".", 1) |
|
668 for child in self.AttributesTree: |
|
669 if child["name"] == parts[0] and len(parts) > 1: |
|
670 result = self.OpenClose(child, parts[1]) |
|
671 if result: |
|
672 self.RefreshAttributesGrid() |
|
673 |
|
674 def OnParamsEnableChanged(self, event): |
|
675 plugin = self.GetSelectedPlugin() |
|
676 if plugin and plugin != self.PluginRoot: |
|
677 plugin.BaseParams.setEnabled(event.Checked()) |
|
678 event.Skip() |
|
679 |
|
680 def OnParamsIECChannelChanged(self, event): |
|
681 plugin = self.GetSelectedPlugin() |
|
682 if plugin and plugin != self.PluginRoot: |
|
683 plugin.BaseParams.setIEC_Channel(self.ParamsIECChannel.GetValue()) |
|
684 event.Skip() |
|
685 |
|
686 def OnParamsTargetTypeChanged(self, event): |
|
687 plugin = self.GetSelectedPlugin() |
|
688 if plugin and plugin == self.PluginRoot: |
|
689 self.PluginRoot.ChangeTargetType(self.ParamsTargetType.GetStringSelection()) |
|
690 self.RefreshAttributesGrid() |
|
691 event.Skip() |
|
692 |
|
693 def OnAttributesGridCellChange(self, event): |
|
694 row = event.GetRow() |
|
695 plugin = self.GetSelectedPlugin() |
|
696 if plugin: |
|
697 path = self.Table.GetValueByName(row, "Path") |
|
698 value = self.Table.GetValueByName(row, "Value") |
|
699 plugin.SetParamsAttribute(path, value) |
|
700 print plugin.GetParamsAttributes(path) |
|
701 self.RefreshAttributesGrid() |
|
702 event.Skip() |
|
703 |
|
704 def OnAttributesGridCellLeftClick(self, event): |
|
705 wx.CallAfter(self.OpenCloseAttribute) |
|
706 event.Skip() |
|
707 |
|
708 def OnNewProjectMenu(self, event): |
612 def OnNewProjectMenu(self, event): |
709 defaultpath = self.PluginRoot.GetProjectPath() |
613 defaultpath = self.PluginRoot.GetProjectPath() |
710 if defaultpath == "": |
614 if defaultpath == "": |
711 defaultpath = os.getcwd() |
615 defaultpath = os.getcwd() |
712 dialog = wx.DirDialog(self , "Choose a project", defaultpath, wx.DD_NEW_DIR_BUTTON) |
616 dialog = wx.DirDialog(self , "Choose a project", defaultpath, wx.DD_NEW_DIR_BUTTON) |
713 if dialog.ShowModal() == wx.ID_OK: |
617 if dialog.ShowModal() == wx.ID_OK: |
714 projectpath = dialog.GetPath() |
618 projectpath = dialog.GetPath() |
715 dialog.Destroy() |
619 dialog.Destroy() |
716 if os.path.isdir(projectpath) and len(os.listdir(projectpath)) == 0: |
620 res = self.PluginRoot.NewProject(projectpath) |
717 dialog = ProjectDialog(self) |
621 if not res : |
718 if dialog.ShowModal() == wx.ID_OK: |
622 self.RefreshPluginTree() |
719 values = dialog.GetValues() |
623 self.RefreshButtons() |
720 values["creationDateTime"] = datetime(*localtime()[:6]) |
624 self.RefreshMainMenu() |
721 self.PluginRoot.NewProject(projectpath, values) |
|
722 self.RefreshPluginTree() |
|
723 self.RefreshButtons() |
|
724 self.RefreshMainMenu() |
|
725 dialog.Destroy() |
|
726 else: |
625 else: |
727 message = wx.MessageDialog(self, "Folder choosen isn't empty. You can't use it for a new project!", "ERROR", wx.OK|wx.ICON_ERROR) |
626 message = wx.MessageDialog(self, res, "ERROR", wx.OK|wx.ICON_ERROR) |
728 message.ShowModal() |
627 message.ShowModal() |
729 message.Destroy() |
628 message.Destroy() |
730 event.Skip() |
629 event.Skip() |
731 |
630 |
732 def OnOpenProjectMenu(self, event): |
631 def OnOpenProjectMenu(self, event): |
733 defaultpath = self.PluginRoot.GetProjectPath() |
632 defaultpath = self.PluginRoot.GetProjectPath() |
734 if defaultpath == "": |
633 if not defaultpath: |
735 defaultpath = os.getcwd() |
634 defaultpath = os.getcwd() |
736 dialog = wx.DirDialog(self , "Choose a project", defaultpath, wx.DD_NEW_DIR_BUTTON) |
635 dialog = wx.DirDialog(self , "Choose a project", defaultpath, wx.DD_NEW_DIR_BUTTON) |
737 if dialog.ShowModal() == wx.ID_OK: |
636 if dialog.ShowModal() == wx.ID_OK: |
738 projectpath = dialog.GetPath() |
637 projectpath = dialog.GetPath() |
739 if os.path.isdir(projectpath): |
638 if os.path.isdir(projectpath): |
826 self.RefreshPluginTree() |
725 self.RefreshPluginTree() |
827 dialog.Destroy() |
726 dialog.Destroy() |
828 |
727 |
829 def DeletePlugin(self): |
728 def DeletePlugin(self): |
830 pass |
729 pass |
831 |
|
832 def EditPLC(self): |
|
833 if not self.PLCEditor: |
|
834 self.PLCEditor = PLCOpenEditor(self, self.PluginRoot.PLCManager) |
|
835 self.PLCEditor.RefreshProjectTree() |
|
836 self.PLCEditor.RefreshFileMenu() |
|
837 self.PLCEditor.RefreshEditMenu() |
|
838 self.PLCEditor.RefreshToolBar() |
|
839 self.PLCEditor.Show() |
|
840 |
|
841 def LogCommand(self, Command, sz_limit = 100): |
|
842 |
|
843 import os, popen2, fcntl, select, signal |
|
844 |
|
845 child = popen2.Popen3(Command, 1) # capture stdout and stderr from command |
|
846 child.tochild.close() # don't need to talk to child |
|
847 outfile = child.fromchild |
|
848 outfd = outfile.fileno() |
|
849 errfile = child.childerr |
|
850 errfd = errfile.fileno() |
|
851 outdata = errdata = '' |
|
852 outeof = erreof = 0 |
|
853 outlen = errlen = 0 |
|
854 while 1: |
|
855 ready = select.select([outfd,errfd],[],[]) # wait for input |
|
856 if outfd in ready[0]: |
|
857 outchunk = outfile.readline() |
|
858 if outchunk == '': outeof = 1 |
|
859 else : outlen += 1 |
|
860 outdata += outchunk |
|
861 self.Log.write(outchunk) |
|
862 if errfd in ready[0]: |
|
863 errchunk = errfile.readline() |
|
864 if errchunk == '': erreof = 1 |
|
865 else : errlen += 1 |
|
866 errdata += errchunk |
|
867 self.Log.write_warning(errchunk) |
|
868 if outeof and erreof : break |
|
869 if errlen > sz_limit or outlen > sz_limit : |
|
870 os.kill(child.pid, signal.SIGTERM) |
|
871 self.Log.write_error("Output size reached limit -- killed\n") |
|
872 break |
|
873 err = child.wait() |
|
874 return (err, outdata, errdata) |
|
875 |
730 |
876 #------------------------------------------------------------------------------- |
731 #------------------------------------------------------------------------------- |
877 # Add Bus Dialog |
732 # Add Bus Dialog |
878 #------------------------------------------------------------------------------- |
733 #------------------------------------------------------------------------------- |
879 |
734 |