Beremiz.py
changeset 20 d3cb5020997b
parent 19 73257cea38bd
child 21 bded6d31365c
equal deleted inserted replaced
19:73257cea38bd 20:d3cb5020997b
    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, 
   297     def __init__(self, parent, projectOpen):
   320     def __init__(self, parent, projectOpen):
   298         self._init_ctrls(parent)
   321         self._init_ctrls(parent)
   299         
   322         
   300         self.Log = LogPseudoFile(self.LogConsole)
   323         self.Log = LogPseudoFile(self.LogConsole)
   301         
   324         
   302         self.PluginRoot = PluginsRoot()
   325         self.PluginRoot = PluginsRoot(self)
   303         
   326         
   304         if projectOpen:
   327         if projectOpen:
   305             self.PluginRoot.LoadProject(projectOpen)
   328             self.PluginRoot.LoadProject(projectOpen)
   306             self.RefreshPluginTree()
   329             self.RefreshPluginTree()
   307         
       
   308         self.PLCEditor = None
       
   309         
   330         
   310         self.RefreshPluginParams()
   331         self.RefreshPluginParams()
   311         self.RefreshButtons()
   332         self.RefreshButtons()
   312         self.RefreshMainMenu()
   333         self.RefreshMainMenu()
   313         
   334         
   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):
   783     def OnDeleteMenu(self, event):
   682     def OnDeleteMenu(self, event):
   784         self.DeletePlugin()
   683         self.DeletePlugin()
   785         event.Skip()
   684         event.Skip()
   786 
   685 
   787     def OnBuildMenu(self, event):
   686     def OnBuildMenu(self, event):
   788         self.BuildAutom()
   687         #self.BuildAutom()
   789         event.Skip()
   688         event.Skip()
   790 
   689 
   791     def OnSimulateMenu(self, event):
   690     def OnSimulateMenu(self, event):
   792         event.Skip()
   691         event.Skip()
   793     
   692     
   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