LPCBeremiz.py
changeset 442 f971de6d274f
child 444 8eb1186fc9cf
equal deleted inserted replaced
441:379c66468cf6 442:f971de6d274f
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 __version__ = "$Revision$"
       
     5 
       
     6 import os, sys, getopt, wx, tempfile
       
     7 from types import TupleType, StringType, UnicodeType
       
     8 
       
     9 CWD = os.path.split(os.path.realpath(__file__))[0]
       
    10 
       
    11 def Bpath(*args):
       
    12     return os.path.join(CWD,*args)
       
    13 
       
    14 if __name__ == '__main__':
       
    15     def usage():
       
    16         print "\nUsage of LPCBeremiz.py :"
       
    17         print "\n   %s [Projectpath] [Buildpath]\n"%sys.argv[0]
       
    18     
       
    19     try:
       
    20         opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
       
    21     except getopt.GetoptError:
       
    22         # print help information and exit:
       
    23         usage()
       
    24         sys.exit(2)
       
    25     
       
    26     for o, a in opts:
       
    27         if o in ("-h", "--help"):
       
    28             usage()
       
    29             sys.exit()
       
    30     
       
    31     if len(args) > 2:
       
    32         usage()
       
    33         sys.exit()
       
    34     elif len(args) == 1:
       
    35         projectOpen = args[0]
       
    36         buildpath = None
       
    37     elif len(args) == 2:
       
    38         projectOpen = args[0]
       
    39         buildpath = args[1]
       
    40     else:
       
    41         projectOpen = None
       
    42         buildpath = None
       
    43 
       
    44 class PseudoLocale:
       
    45     LocaleDirs = []
       
    46     Domains = []
       
    47     
       
    48     def AddCatalogLookupPathPrefix(self, localedir):
       
    49         self.LocaleDirs.append(localedir)
       
    50     
       
    51     def AddCatalog(self, domain):
       
    52         self.Domains.append(domain)
       
    53 
       
    54 # Import module for internationalization
       
    55 import gettext
       
    56 import __builtin__
       
    57 
       
    58 # Define locale for wx
       
    59 __builtin__.__dict__['loc'] = PseudoLocale()
       
    60 
       
    61 if __name__ == '__main__':
       
    62     __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
       
    63 
       
    64 from Beremiz import *
       
    65 from plugger import PluginsRoot, PlugTemplate, opjimg
       
    66 from plcopen.structures import LOCATIONDATATYPES
       
    67 from PLCControler import LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP,\
       
    68                          LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
       
    69 from PLCOpenEditor import IDEFrame
       
    70 
       
    71 #-------------------------------------------------------------------------------
       
    72 #                              LPCModule Class
       
    73 #-------------------------------------------------------------------------------
       
    74 
       
    75 def _GetModuleChildren(module):
       
    76     children = []
       
    77     for child in module["children"]:
       
    78         if child["type"] == LOCATION_GROUP:
       
    79             children.extend(child["children"])
       
    80         else:
       
    81             children.append(child)
       
    82     return children
       
    83 
       
    84 def _GetLastModuleGroup(module):
       
    85     group = module
       
    86     for child in module["children"]:
       
    87         if child["type"] == LOCATION_GROUP:
       
    88             group = child
       
    89     return group["children"]
       
    90 
       
    91 def _GetModuleBySomething(module, something, toks):
       
    92     for child in _GetModuleChildren(module):
       
    93         if child.get(something) == toks[0]:
       
    94             if len(toks) > 1:
       
    95                 return _GetModuleBySomething(child, something, toks[1:])
       
    96             return child
       
    97     return None
       
    98 
       
    99 def _GetModuleVariable(module, location):
       
   100     for child in _GetModuleChildren(module):
       
   101         if child["location"] == location:
       
   102             return child
       
   103     return None
       
   104 
       
   105 def _RemoveModuleChild(module, child):
       
   106     if child in module["children"]:
       
   107         module["children"].remove(child)
       
   108     else:
       
   109         for group in module["children"]:
       
   110             if group["type"] == LOCATION_GROUP and child in group["children"]:
       
   111                 group["children"].remove(child)
       
   112 
       
   113 LOCATION_TYPES = {"I": LOCATION_VAR_INPUT,
       
   114                   "Q": LOCATION_VAR_OUTPUT,
       
   115                   "M": LOCATION_VAR_MEMORY}
       
   116 
       
   117 LOCATION_DIRS = dict([(dir, size) for size, dir in LOCATION_TYPES.iteritems()])
       
   118 
       
   119 LOCATION_SIZES = {}
       
   120 for size, types in LOCATIONDATATYPES.iteritems():
       
   121     for type in types:
       
   122         LOCATION_SIZES[type] = size
       
   123 
       
   124 BUS_TEXT = """/* Code generated by LPCBus plugin */
       
   125 
       
   126 /* LPCBus plugin includes */
       
   127 #include "app_glue.h"
       
   128 #ifdef _WINDOWS_H
       
   129   #include "iec_types.h"
       
   130 #else
       
   131   #include "iec_std_lib.h"
       
   132 #endif
       
   133 
       
   134 %(declare_code)s
       
   135 
       
   136 /* LPCBus plugin user variables definition */
       
   137 %(var_decl)s
       
   138 
       
   139 /* LPCBus plugin functions */
       
   140 int __init_%(location_str)s(int argc,char **argv)
       
   141 {
       
   142   return 0;
       
   143 }
       
   144 
       
   145 void __cleanup_%(location_str)s(void)
       
   146 {
       
   147 }
       
   148 
       
   149 void __retrieve_%(location_str)s(void)
       
   150 {
       
   151   %(retrieve_code)s
       
   152 }
       
   153         
       
   154 void __publish_%(location_str)s(void)
       
   155 {
       
   156   %(publish_code)s
       
   157 }
       
   158 """
       
   159 
       
   160 class LPCBus(object):
       
   161     
       
   162     def __init__(self):
       
   163         self.VariableLocationTree = []
       
   164         self.ResetUsedLocations()
       
   165         self.Icon = None
       
   166     
       
   167     def __getitem__(self, key):
       
   168         if key == "children":
       
   169             return self.VariableLocationTree
       
   170         raise KeyError, "Only 'children' key is available"
       
   171     
       
   172     def SetIcon(self, icon):
       
   173         self.Icon = icon
       
   174     
       
   175     def _GetChildBySomething(self, something, toks):
       
   176         return _GetModuleBySomething({"children" : self.VariableLocationTree}, something, toks)
       
   177     
       
   178     def GetBaseTypes(self):
       
   179         return self.GetPlugRoot().GetBaseTypes()
       
   180 
       
   181     def GetSizeOfType(self, type):
       
   182         return LOCATION_SIZES[self.GetPlugRoot().GetBaseType(type)]
       
   183     
       
   184     def _GetVariableLocationTree(self, current_location, infos):
       
   185         if infos["type"] == LOCATION_MODULE:
       
   186             location = current_location + (infos["IEC_Channel"],)
       
   187             return {"name": infos["name"],
       
   188                     "type": infos["type"],
       
   189                     "location": ".".join(map(str, location + ("x",))), 
       
   190                     "icon": infos["icon"], 
       
   191                     "children": [self._GetVariableLocationTree(location, child) for child in infos["children"]]}
       
   192         elif infos["type"] == LOCATION_GROUP:
       
   193             return {"name": infos["name"],
       
   194                     "type": infos["type"],
       
   195                     "location": "", 
       
   196                     "icon": infos["icon"], 
       
   197                     "children": [self._GetVariableLocationTree(current_location, child) for child in infos["children"]]}
       
   198         else:
       
   199             size = self.GetSizeOfType(infos["IEC_type"])
       
   200             location = "%" + LOCATION_DIRS[infos["type"]] + size + ".".join(map(str, current_location + infos["location"]))
       
   201             return {"name": infos["name"],
       
   202                     "type": infos["type"],
       
   203                     "size": size,
       
   204                     "IEC_type": infos["IEC_type"],
       
   205                     "location": location,
       
   206                     "description": infos["description"],
       
   207                     "children": []}
       
   208     
       
   209     def GetVariableLocationTree(self):
       
   210         return {"name": self.BaseParams.getName(),
       
   211                 "type": LOCATION_PLUGIN,
       
   212                 "location": self.GetFullIEC_Channel(),
       
   213                 "icon": self.Icon, 
       
   214                 "children": [self._GetVariableLocationTree(self.GetCurrentLocation(), child) 
       
   215                              for child in self.VariableLocationTree]}
       
   216     
       
   217     def PlugTestModified(self):
       
   218         return False
       
   219 
       
   220     def PlugMakeDir(self):
       
   221         pass
       
   222 
       
   223     def PlugRequestSave(self):
       
   224         return None
       
   225 
       
   226     def ResetUsedLocations(self):
       
   227         self.UsedLocations = {}
       
   228     
       
   229     def _AddUsedLocation(self, parent, location):
       
   230         num = location.pop(0)
       
   231         if not parent.has_key(num):
       
   232             parent[num] = {"used": False, "children": {}}
       
   233         if len(location) > 0:
       
   234             self._AddUsedLocation(parent[num]["children"], location)
       
   235         else:
       
   236             parent[num]["used"] = True
       
   237         
       
   238     def AddUsedLocation(self, location):
       
   239         if len(location) > 0:
       
   240             self._AddUsedLocation(self.UsedLocations, list(location))
       
   241 
       
   242     def _CheckLocationConflicts(self, parent, location):
       
   243         num = location.pop(0)
       
   244         if not parent.has_key(num):
       
   245             return False
       
   246         if len(location) > 0:
       
   247             if parent[num]["used"]:
       
   248                 return True
       
   249             return self._CheckLocationConflicts(parent[num]["children"], location)
       
   250         elif len(parent[num]["children"]) > 0:
       
   251             return True
       
   252         return False
       
   253 
       
   254     def CheckLocationConflicts(self, location):
       
   255         if len(location) > 0:
       
   256             return self._CheckLocationConflicts(self.UsedLocations, list(location))
       
   257         return False
       
   258 
       
   259     def PlugGenerate_C(self, buildpath, locations):
       
   260         """
       
   261         Generate C code
       
   262         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
       
   263         @param locations: List of complete variables locations \
       
   264             [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
       
   265             "NAME" : name of the variable (generally "__IW0_1_2" style)
       
   266             "DIR" : direction "Q","I" or "M"
       
   267             "SIZE" : size "X", "B", "W", "D", "L"
       
   268             "LOC" : tuple of interger for IEC location (0,1,2,...)
       
   269             }, ...]
       
   270         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
       
   271         """
       
   272         current_location = self.GetCurrentLocation()
       
   273         # define a unique name for the generated C file
       
   274         location_str = "_".join(map(str, current_location))
       
   275         
       
   276         code_str = {"location_str": location_str,
       
   277                     "var_decl": "",
       
   278                     "declare_code": "",
       
   279                     "retrieve_code": "",
       
   280                     "publish_code": "",
       
   281                    }
       
   282         
       
   283         # Adding variables
       
   284         vars = []
       
   285         self.ResetUsedLocations()
       
   286         for location in locations:
       
   287             loc = location["LOC"][len(current_location):]
       
   288             group = next = self
       
   289             i = 0
       
   290             while next is not None and i < len(loc):
       
   291                 next = self._GetChildBySomething("IEC_Channel", loc[:i + 1])
       
   292                 if next is not None:
       
   293                     i += 1
       
   294                     group = next
       
   295             var_loc = loc[i:]
       
   296             for variable in group["children"]:
       
   297                 if variable["location"] == var_loc:
       
   298                     if location["DIR"] != LOCATION_DIRS[variable["type"]]:
       
   299                         raise Exception, "Direction conflict in variable definition"
       
   300                     if location["IEC_TYPE"] != variable["IEC_type"]:
       
   301                         raise Exception, "Type conflict in variable definition"
       
   302                     if location["DIR"] == "Q":
       
   303                         if self.CheckLocationConflicts(location["LOC"]):
       
   304                             raise Exception, "BYTE and BIT from the same BYTE can't be used together"
       
   305                         self.AddUsedLocation(location["LOC"])
       
   306                     vars.append({"location": location["NAME"],
       
   307                                  "Type": variable["IEC_type"],
       
   308                                  "Declare": variable["declare"],
       
   309                                  "Retrieve": variable["retrieve"],
       
   310                                  "Publish": variable["publish"],
       
   311                                 })
       
   312                     break
       
   313         base_types = self.GetPlugRoot().GetBaseTypes()
       
   314         for var in vars:
       
   315             prefix = ""
       
   316             if var["Type"] in base_types:
       
   317                 prefix = "IEC_"
       
   318             code_str["var_decl"] += "%s%s beremiz%s;\n"%(prefix, var["Type"], var["location"])
       
   319             code_str["var_decl"] += "%s%s *%s = &beremiz%s;\n"%(prefix, var["Type"], var["location"], var["location"])
       
   320             if var["Declare"] != "":
       
   321                 code_str["declare_code"] += var["Declare"] + "\n"
       
   322             if var["Retrieve"] != "":
       
   323                 code_str["retrieve_code"] += var["Retrieve"] % ("*" + var["location"]) + "\n"
       
   324             if var["Publish"] != "":
       
   325                 code_str["publish_code"] += var["Publish"] % ("*" + var["location"]) + "\n"
       
   326         
       
   327         Gen_Module_path = os.path.join(buildpath, "Module_%s.c"%location_str)
       
   328         module = open(Gen_Module_path,'w')
       
   329         module.write(BUS_TEXT % code_str)
       
   330         module.close()
       
   331         
       
   332         matiec_flags = '"-I%s"'%os.path.abspath(self.GetPlugRoot().GetIECLibPath())
       
   333         return [(Gen_Module_path, matiec_flags)],"",True
       
   334 
       
   335 #-------------------------------------------------------------------------------
       
   336 #                              LPCPluginsRoot Class
       
   337 #-------------------------------------------------------------------------------
       
   338 
       
   339 def mycopytree(src, dst):
       
   340     """
       
   341     Copy content of a directory to an other, omit hidden files
       
   342     @param src: source directory
       
   343     @param dst: destination directory
       
   344     """
       
   345     for i in os.listdir(src):
       
   346         if not i.startswith('.'):
       
   347             srcpath = os.path.join(src,i)
       
   348             dstpath = os.path.join(dst,i)
       
   349             if os.path.isdir(srcpath):
       
   350                 os.makedirs(dstpath)
       
   351                 mycopytree(srcpath, dstpath)
       
   352             elif os.path.isfile(srcpath):
       
   353                 shutil.copy2(srcpath, dstpath)
       
   354 
       
   355 class LPCPluginsRoot(PluginsRoot):
       
   356 
       
   357     PlugChildsTypes = [("LPCBus", LPCBus, "LPC bus")]
       
   358 
       
   359     PluginMethods = [
       
   360         {"bitmap" : opjimg("Build"),
       
   361          "name" : _("Build"),
       
   362          "tooltip" : _("Build project into build folder"),
       
   363          "method" : "_build"},
       
   364         {"bitmap" : opjimg("Clean"),
       
   365          "name" : _("Clean"),
       
   366          "enabled" : False,
       
   367          "tooltip" : _("Clean project build folder"),
       
   368          "method" : "_Clean"},
       
   369     ]
       
   370 
       
   371     def GetProjectName(self):
       
   372         return self.Project.getname()
       
   373 
       
   374     def GetDefaultTarget(self):
       
   375         target = self.Classes["BeremizRoot_TargetType"]()
       
   376         target_value = self.Classes["TargetType_Makefile"]()
       
   377         target_value.setBuildPath(self.BuildPath)
       
   378         target.setcontent({"name": "Makefile", "value": target_value})
       
   379         return target
       
   380      
       
   381     def SetProjectName(self, name):
       
   382         return self.Project.setname(name)
       
   383 
       
   384     def LoadProject(self, ProjectPath, BuildPath=None):
       
   385         """
       
   386         Load a project XML file
       
   387         @param ProjectPath: path of the project xml file
       
   388         """
       
   389         # Load PLCOpen file
       
   390         result = self.OpenXMLFile(ProjectPath)
       
   391         if result:
       
   392             return result
       
   393         # Change XSD into class members
       
   394         self._AddParamsMembers()
       
   395         self.PluggedChilds = {}
       
   396         # Keep track of the root plugin (i.e. project path)
       
   397         self.ProjectPath = ProjectPath
       
   398 
       
   399         self.BuildPath = tempfile.mkdtemp()
       
   400         if BuildPath is not None:
       
   401             mycopytree(BuildPath, self.BuildPath)
       
   402         
       
   403         self.RefreshPluginsBlockLists()
       
   404         
       
   405         if os.path.exists(self._getBuildPath()):
       
   406             self.EnableMethod("_Clean", True)
       
   407         
       
   408     def SaveProject(self):
       
   409         self.SaveXMLFile(self.ProjectPath)
       
   410 
       
   411 #-------------------------------------------------------------------------------
       
   412 #                              LPCBeremiz Class
       
   413 #-------------------------------------------------------------------------------
       
   414 
       
   415 class LPCBeremiz(Beremiz):
       
   416     
       
   417     def _init_coll_FileMenu_Items(self, parent):
       
   418         AppendMenu(parent, help='', id=wx.ID_SAVE,
       
   419               kind=wx.ITEM_NORMAL, text=_(u'Save\tCTRL+S'))
       
   420         AppendMenu(parent, help='', id=wx.ID_CLOSE,
       
   421               kind=wx.ITEM_NORMAL, text=_(u'Close Tab\tCTRL+W'))
       
   422         parent.AppendSeparator()
       
   423         AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP,
       
   424               kind=wx.ITEM_NORMAL, text=_(u'Page Setup'))
       
   425         AppendMenu(parent, help='', id=wx.ID_PREVIEW,
       
   426               kind=wx.ITEM_NORMAL, text=_(u'Preview'))
       
   427         AppendMenu(parent, help='', id=wx.ID_PRINT,
       
   428               kind=wx.ITEM_NORMAL, text=_(u'Print'))
       
   429         parent.AppendSeparator()
       
   430         AppendMenu(parent, help='', id=wx.ID_PROPERTIES,
       
   431               kind=wx.ITEM_NORMAL, text=_(u'Properties'))
       
   432         parent.AppendSeparator()
       
   433         AppendMenu(parent, help='', id=wx.ID_EXIT,
       
   434               kind=wx.ITEM_NORMAL, text=_(u'Quit\tCTRL+Q'))
       
   435         
       
   436         self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
       
   437         self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE)
       
   438         self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
       
   439         self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
       
   440         self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
       
   441         self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES)
       
   442         self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
       
   443     
       
   444     def _init_ctrls(self, prnt):
       
   445         IDEFrame._init_ctrls(self, prnt)
       
   446         
       
   447         self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=ID_BEREMIZINSPECTOR)
       
   448         accel = wx.AcceleratorTable([wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), ID_BEREMIZINSPECTOR)])
       
   449         self.SetAcceleratorTable(accel)
       
   450         
       
   451         self.PLCConfig = wx.ScrolledWindow(id=ID_BEREMIZPLCCONFIG,
       
   452               name='PLCConfig', parent=self.LeftNoteBook, pos=wx.Point(0, 0),
       
   453               size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER|wx.HSCROLL|wx.VSCROLL)
       
   454         self.PLCConfig.SetBackgroundColour(wx.WHITE)
       
   455         self.PLCConfig.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
       
   456         self.PLCConfig.Bind(wx.EVT_SIZE, self.OnMoveWindow)
       
   457         self.LeftNoteBook.InsertPage(0, self.PLCConfig, _("Topology"), True)
       
   458         
       
   459         self.LogConsole = wx.TextCtrl(id=ID_BEREMIZLOGCONSOLE, value='',
       
   460                   name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0),
       
   461                   size=wx.Size(0, 0), style=wx.TE_MULTILINE|wx.TE_RICH2)
       
   462         self.LogConsole.Bind(wx.EVT_LEFT_DCLICK, self.OnLogConsoleDClick)
       
   463         self.BottomNoteBook.AddPage(self.LogConsole, _("Log Console"))
       
   464         
       
   465         self._init_beremiz_sizers()
       
   466 
       
   467     def OnCloseFrame(self, event):
       
   468         global frame, lpcberemiz_cmd
       
   469         frame = None
       
   470         self.PluginRoot.ResetAppFrame(lpcberemiz_cmd.Log)
       
   471         
       
   472         self.KillLocalRuntime()
       
   473         
       
   474         event.Skip()
       
   475 
       
   476     def RefreshFileMenu(self):
       
   477         if self.PluginRoot is not None:
       
   478             selected = self.TabsOpened.GetSelection()
       
   479             if selected >= 0:
       
   480                 graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer)
       
   481             else:
       
   482                 graphic_viewer = False
       
   483             if self.TabsOpened.GetPageCount() > 0:
       
   484                 self.FileMenu.Enable(wx.ID_CLOSE, True)
       
   485                 if graphic_viewer:
       
   486                     self.FileMenu.Enable(wx.ID_PREVIEW, True)
       
   487                     self.FileMenu.Enable(wx.ID_PRINT, True)
       
   488                 else:
       
   489                     self.FileMenu.Enable(wx.ID_PREVIEW, False)
       
   490                     self.FileMenu.Enable(wx.ID_PRINT, False)
       
   491             else:
       
   492                 self.FileMenu.Enable(wx.ID_CLOSE, False)
       
   493                 self.FileMenu.Enable(wx.ID_PREVIEW, False)
       
   494                 self.FileMenu.Enable(wx.ID_PRINT, False)
       
   495             self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
       
   496             self.FileMenu.Enable(wx.ID_SAVE, True)
       
   497             self.FileMenu.Enable(wx.ID_PROPERTIES, True)
       
   498         else:
       
   499             self.FileMenu.Enable(wx.ID_CLOSE, False)
       
   500             self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
       
   501             self.FileMenu.Enable(wx.ID_PREVIEW, False)
       
   502             self.FileMenu.Enable(wx.ID_PRINT, False)
       
   503             self.FileMenu.Enable(wx.ID_SAVE, False)
       
   504             self.FileMenu.Enable(wx.ID_PROPERTIES, False)
       
   505         
       
   506     def RefreshPLCParams(self):
       
   507         self.Freeze()
       
   508         self.ClearSizer(self.PLCParamsSizer)
       
   509         
       
   510         if self.PluginRoot is not None:    
       
   511             plcwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
       
   512             if self.PluginRoot.PlugTestModified():
       
   513                 bkgdclr = CHANGED_TITLE_COLOUR
       
   514             else:
       
   515                 bkgdclr = TITLE_COLOUR
       
   516                 
       
   517             if self.PluginRoot not in self.PluginInfos:
       
   518                 self.PluginInfos[self.PluginRoot] = {"right_visible" : False}
       
   519             
       
   520             plcwindow.SetBackgroundColour(TITLE_COLOUR)
       
   521             plcwindow.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
       
   522             self.PLCParamsSizer.AddWindow(plcwindow, 0, border=0, flag=wx.GROW)
       
   523             
       
   524             plcwindowsizer = wx.BoxSizer(wx.HORIZONTAL)
       
   525             plcwindow.SetSizer(plcwindowsizer)
       
   526             
       
   527             st = wx.StaticText(plcwindow, -1)
       
   528             st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
       
   529             st.SetLabel(self.PluginRoot.GetProjectName())
       
   530             plcwindowsizer.AddWindow(st, 0, border=5, flag=wx.ALL|wx.ALIGN_CENTER)
       
   531             
       
   532             plcwindowmainsizer = wx.BoxSizer(wx.VERTICAL)
       
   533             plcwindowsizer.AddSizer(plcwindowmainsizer, 0, border=5, flag=wx.ALL)
       
   534             
       
   535             plcwindowbuttonsizer = wx.BoxSizer(wx.HORIZONTAL)
       
   536             plcwindowmainsizer.AddSizer(plcwindowbuttonsizer, 0, border=0, flag=wx.ALIGN_CENTER)
       
   537             
       
   538             msizer = self.GenerateMethodButtonSizer(self.PluginRoot, plcwindow, not self.PluginInfos[self.PluginRoot]["right_visible"])
       
   539             plcwindowbuttonsizer.AddSizer(msizer, 0, border=0, flag=wx.GROW)
       
   540             
       
   541         self.PLCConfigMainSizer.Layout()
       
   542         self.RefreshScrollBars()
       
   543         self.Thaw()
       
   544 
       
   545     def GenerateTreeBranch(self, plugin):
       
   546         leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
       
   547         if plugin.PlugTestModified():
       
   548             bkgdclr=CHANGED_WINDOW_COLOUR
       
   549         else:
       
   550             bkgdclr=WINDOW_COLOUR
       
   551 
       
   552         leftwindow.SetBackgroundColour(bkgdclr)
       
   553         
       
   554         if plugin not in self.PluginInfos:
       
   555             self.PluginInfos[plugin] = {"expanded" : False, "left_visible" : False, "right_visible" : False}
       
   556             
       
   557         self.PluginInfos[plugin]["children"] = plugin.IECSortedChilds()
       
   558         plugin_infos = plugin.GetVariableLocationTree()
       
   559         plugin_locations = []
       
   560         if len(self.PluginInfos[plugin]["children"]) == 0:
       
   561             plugin_locations = plugin_infos["children"]
       
   562             if not self.PluginInfos[plugin].has_key("locations_infos"):
       
   563                 self.PluginInfos[plugin]["locations_infos"] = {"root": {"expanded" : False}}
       
   564                 
       
   565             self.PluginInfos[plugin]["locations_infos"]["root"]["children"] = []
       
   566         
       
   567         self.PluginTreeSizer.AddWindow(leftwindow, 0, border=0, flag=wx.GROW)
       
   568         
       
   569         leftwindowsizer = wx.BoxSizer(wx.HORIZONTAL)
       
   570         leftwindow.SetSizer(leftwindowsizer)
       
   571                 
       
   572         st = wx.StaticText(leftwindow, -1)
       
   573         st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
       
   574         st.SetLabel(plugin.GetFullIEC_Channel())
       
   575         leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT)
       
   576         
       
   577         expandbutton_id = wx.NewId()
       
   578         expandbutton = wx.lib.buttons.GenBitmapToggleButton(id=expandbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'plus.png')),
       
   579               name='ExpandButton', parent=leftwindow, pos=wx.Point(0, 0),
       
   580               size=wx.Size(13, 13), style=wx.NO_BORDER)
       
   581         expandbutton.labelDelta = 0
       
   582         expandbutton.SetBezelWidth(0)
       
   583         expandbutton.SetUseFocusIndicator(False)
       
   584         expandbutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'minus.png')))
       
   585             
       
   586         if len(self.PluginInfos[plugin]["children"]) > 0:
       
   587             expandbutton.SetToggle(self.PluginInfos[plugin]["expanded"])
       
   588             def togglebutton(event):
       
   589                 if expandbutton.GetToggle():
       
   590                     self.ExpandPlugin(plugin)
       
   591                 else:
       
   592                     self.CollapsePlugin(plugin)
       
   593                 self.PluginInfos[plugin]["expanded"] = expandbutton.GetToggle()
       
   594                 self.PLCConfigMainSizer.Layout()
       
   595                 self.RefreshScrollBars()
       
   596                 event.Skip()
       
   597             expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id)
       
   598         elif len(plugin_locations) > 0:
       
   599             locations_infos = self.PluginInfos[plugin]["locations_infos"]
       
   600             expandbutton.SetToggle(locations_infos["root"]["expanded"])
       
   601             def togglebutton(event):
       
   602                 if expandbutton.GetToggle():
       
   603                     self.ExpandLocation(locations_infos, "root")
       
   604                 else:
       
   605                     self.CollapseLocation(locations_infos, "root")
       
   606                 self.PluginInfos[plugin]["expanded"] = expandbutton.GetToggle()
       
   607                 locations_infos["root"]["expanded"] = expandbutton.GetToggle()
       
   608                 self.PLCConfigMainSizer.Layout()
       
   609                 self.RefreshScrollBars()
       
   610                 event.Skip()
       
   611             expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id)
       
   612         else:
       
   613             expandbutton.Enable(False)
       
   614         leftwindowsizer.AddWindow(expandbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
       
   615         
       
   616         sb = wx.StaticBitmap(leftwindow, -1)
       
   617         icon = plugin_infos.get("icon", None)
       
   618         if icon is None:
       
   619             icon = os.path.join(base_folder, "plcopeneditor", 'Images', '%s.png' % self.LOCATION_BITMAP[plugin_infos["type"]])
       
   620         sb.SetBitmap(wx.Bitmap(icon))
       
   621         leftwindowsizer.AddWindow(sb, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
       
   622         
       
   623         st_id = wx.NewId()
       
   624         st = wx.StaticText(leftwindow, st_id, size=wx.DefaultSize, style=wx.NO_BORDER)
       
   625         st.SetFont(wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
       
   626         st.SetLabel(plugin.MandatoryParams[1].getName())
       
   627         leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
       
   628         
       
   629         rightwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
       
   630         rightwindow.SetBackgroundColour(bkgdclr)
       
   631         
       
   632         self.PluginTreeSizer.AddWindow(rightwindow, 0, border=0, flag=wx.GROW)
       
   633 
       
   634         self.PluginInfos[plugin]["left"] = leftwindow
       
   635         self.PluginInfos[plugin]["right"] = rightwindow
       
   636         for child in self.PluginInfos[plugin]["children"]:
       
   637             self.GenerateTreeBranch(child)
       
   638             if not self.PluginInfos[child]["expanded"]:
       
   639                 self.CollapsePlugin(child)
       
   640         if len(plugin_locations) > 0:
       
   641             locations_infos = self.PluginInfos[plugin]["locations_infos"]
       
   642             for location in plugin_locations:
       
   643                 locations_infos["root"]["children"].append("root.%s" % location["name"])
       
   644                 self.GenerateLocationTreeBranch(locations_infos, "root", location)
       
   645             if not locations_infos["root"]["expanded"]:
       
   646                 self.CollapseLocation(locations_infos, "root")
       
   647 
       
   648 frame = None
       
   649 
       
   650 def BeremizStartProc(plugin_root):
       
   651     global frame
       
   652 
       
   653     app = wx.PySimpleApp()
       
   654     app.SetAppName('beremiz')
       
   655     wx.InitAllImageHandlers()
       
   656     
       
   657     # Get the english language
       
   658     langid = wx.LANGUAGE_ENGLISH
       
   659     # Import module for internationalization
       
   660     import gettext
       
   661     import __builtin__
       
   662     
       
   663     # Define locale for wx
       
   664     loc = wx.Locale(langid)
       
   665     for localedir in PseudoLocale.LocaleDirs:
       
   666         loc.AddCatalogLookupPathPrefix(localedir)
       
   667     for domain in PseudoLocale.Domains:
       
   668         loc.AddCatalog(domain)
       
   669     
       
   670     __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
       
   671     
       
   672     # Install a exception handle for bug reports
       
   673     AddExceptHook(os.getcwd(),__version__)
       
   674 
       
   675     frame = LPCBeremiz(None, plugin_root=plugin_root, debug=False)
       
   676     plugin_root.SetAppFrame(frame, frame.Log)
       
   677     frame.Show()
       
   678     
       
   679     app.MainLoop()
       
   680 
       
   681 class StdoutPseudoFile:
       
   682     """ Base class for file like objects to facilitate StdOut for the Shell."""
       
   683     def write(self, s, style = None):
       
   684         if s != '':
       
   685             print s
       
   686         
       
   687     def write_warning(self, s):
       
   688         self.write(s)
       
   689 
       
   690     def write_error(self, s):
       
   691         self.write(s)
       
   692 
       
   693     def flush(self):
       
   694         pass
       
   695     
       
   696     def isatty(self):
       
   697         return false
       
   698 
       
   699 if __name__ == '__main__':
       
   700     
       
   701     from threading import Thread, Timer
       
   702     import cmd
       
   703 
       
   704     class LPCBeremiz_Cmd(cmd.Cmd):
       
   705         
       
   706         prompt = ""
       
   707         Log = StdoutPseudoFile()
       
   708         RefreshTimer = None
       
   709         
       
   710         def __init__(self, projectOpen, buildpath):
       
   711             cmd.Cmd.__init__(self)
       
   712             self.PluginRoot = LPCPluginsRoot(None, self.Log)
       
   713             if projectOpen is not None and os.path.isfile(projectOpen):
       
   714                 result = self.PluginRoot.LoadProject(projectOpen, buildpath)
       
   715                 if result:
       
   716                     print "Error: Invalid project directory", result
       
   717             else:
       
   718                 print "Error: No such file or directory"
       
   719             
       
   720         def RestartTimer(self):
       
   721             if self.RefreshTimer is not None:
       
   722                 self.RefreshTimer.cancel()
       
   723             self.RefreshTimer = Timer(0.1, self.Refresh)
       
   724             self.RefreshTimer.start()
       
   725         
       
   726         def Exit(self):
       
   727             self.Close()
       
   728             return True
       
   729         
       
   730         def do_EOF(self, line):
       
   731             return self.Exit()
       
   732         
       
   733         def Show(self):
       
   734             beremiz_thread=Thread(target=BeremizStartProc, args=[self.PluginRoot])
       
   735             beremiz_thread.start()
       
   736         
       
   737         def Refresh(self):
       
   738             global frame
       
   739             if frame is not None:
       
   740                 wx.CallAfter(frame.RefreshAll)
       
   741         
       
   742         def Close(self):
       
   743             global frame
       
   744             
       
   745             self.PluginRoot.ResetAppFrame(self.Log)
       
   746             if frame is not None:
       
   747                 wx.CallAfter(frame.Close)
       
   748         
       
   749         def Compile(self):
       
   750             if wx.GetApp() is None:
       
   751                 self.PluginRoot._build()
       
   752             else:
       
   753                 wx.CallAfter(self.PluginRoot._build)
       
   754         
       
   755         def SetProjectName(self, name):
       
   756             self.PluginRoot.SetProjectName(name)
       
   757             self.RestartTimer()
       
   758         
       
   759         def AddBus(self, iec_channel, name, icon=None):
       
   760             for child in self.PluginRoot.IterChilds():
       
   761                 if child.BaseParams.getName() == name:
       
   762                     return "Error: A bus named %s already exists" % name
       
   763                 elif child.BaseParams.getIEC_Channel() == iec_channel:
       
   764                     return "Error: A bus with IEC_channel %d already exists" % iec_channel
       
   765             bus = self.PluginRoot.PlugAddChild(name, "LPCBus", iec_channel)
       
   766             if bus is None:
       
   767                 return "Error: Unable to create bus"
       
   768             bus.SetIcon(icon)
       
   769             self.RestartTimer()
       
   770         
       
   771         def RenameBus(self, iec_channel, name):
       
   772             bus = self.PluginRoot.GetChildByIECLocation((iec_channel,))
       
   773             if bus is None:
       
   774                 return "Error: No bus found"
       
   775             for child in self.PluginRoot.IterChilds():
       
   776                 if child != bus and child.BaseParams.getName() == name:
       
   777                     return "Error: A bus named %s already exists" % name
       
   778             bus.BaseParams.setName(name)
       
   779             self.RestartTimer()
       
   780         
       
   781         def ChangeBusIECChannel(self, old_iec_channel, new_iec_channel):
       
   782             bus = self.PluginRoot.GetChildByIECLocation((old_iec_channel,))
       
   783             if bus is None:
       
   784                 return "Error: No bus found"
       
   785             for child in self.PluginRoot.IterChilds():
       
   786                 if child != bus and child.BaseParams.getIEC_Channel() == new_iec_channel:
       
   787                     return "Error: A bus with IEC_channel %d already exists" % new_iec_channel
       
   788             bus.BaseParams.setIEC_Channel(new_iec_channel)
       
   789             self.RestartTimer()
       
   790         
       
   791         def RemoveBus(self, iec_channel):
       
   792             bus = self.PluginRoot.GetChildByIECLocation((iec_channel,))
       
   793             if bus is None:
       
   794                 return "Error: No bus found"
       
   795             self.PluginRoot.PluggedChilds["LPCBus"].remove(bus)
       
   796             self.RestartTimer()
       
   797     
       
   798         def AddModule(self, parent, iec_channel, name, icon=None):
       
   799             module = self.PluginRoot.GetChildByIECLocation(parent)
       
   800             if module is None:
       
   801                 return "Error: No parent found"
       
   802             for child in _GetModuleChildren(module):
       
   803                 if child["name"] == name:
       
   804                     return "Error: A module named %s already exists" % name
       
   805                 elif child["IEC_Channel"] == iec_channel:
       
   806                     return "Error: A module with IEC_channel %d already exists" % iec_channel 
       
   807             _GetLastModuleGroup(module).append({"name": name, 
       
   808                                                 "type": LOCATION_MODULE, 
       
   809                                                 "IEC_Channel": iec_channel, 
       
   810                                                 "icon": icon, 
       
   811                                                 "children": []})
       
   812             self.RestartTimer()
       
   813     
       
   814         def RenameModule(self, iec_location, name):
       
   815             module = self.PluginRoot.GetChildByIECLocation(iec_location)
       
   816             if module is None:
       
   817                 return "Error: No module found"
       
   818             parent = self.PluginRoot.GetChildByIECLocation(iec_location[:-1])
       
   819             if parent is self.PluginRoot:
       
   820                 return "Error: No module found"
       
   821             if module["name"] != name:
       
   822                 for child in _GetModuleChildren(parent):
       
   823                     if child["name"] == name:
       
   824                         return "Error: A module named %s already exists" % name
       
   825                 module["name"] = name
       
   826             self.RestartTimer()
       
   827     
       
   828         def ChangeModuleIECChannel(self, old_iec_location, new_iec_channel):
       
   829             module = self.PluginRoot.GetChildByIECLocation(old_iec_location)
       
   830             if module is None:
       
   831                 return "Error: No module found"
       
   832             parent = self.PluginRoot.GetChildByIECLocation(old_iec_location[:-1])
       
   833             if parent is self.PluginRoot:
       
   834                 return "Error: No module found"
       
   835             if module["IEC_Channel"] != new_iec_channel:
       
   836                 for child in _GetModuleChildren(parent):
       
   837                     if child["IEC_Channel"] == new_iec_channel:
       
   838                         return "Error: A module with IEC_channel %d already exists" % new_iec_channel
       
   839             module["IEC_Channel"] = new_iec_channel
       
   840             self.RestartTimer()
       
   841     
       
   842         def RemoveModule(self, parent, iec_channel):
       
   843             module = self.PluginRoot.GetChildByIECLocation(parent)
       
   844             if module is None:
       
   845                 return "Error: No parent found"
       
   846             child = _GetModuleBySomething(module, "IEC_Channel", (iec_channel,))
       
   847             if child is None:
       
   848                 return "Error: No module found"
       
   849             _RemoveModuleChild(module, child)
       
   850             self.RestartTimer()
       
   851         
       
   852         def StartGroup(self, parent, name, icon=None):
       
   853             module = self.PluginRoot.GetChildByIECLocation(parent)
       
   854             if module is None:
       
   855                 return "Error: No parent found"
       
   856             for child in module["children"]:
       
   857                 if child["type"] == LOCATION_GROUP and child["name"] == name:
       
   858                     return "Error: A group named %s already exists" % name
       
   859             module["children"].append({"name": name, 
       
   860                                       "type": LOCATION_GROUP, 
       
   861                                       "icon": icon, 
       
   862                                       "children": []})
       
   863             self.RestartTimer()
       
   864     
       
   865         def AddVariable(self, parent, location, name, direction, type, dcode, rcode, pcode, description=""):
       
   866             module = self.PluginRoot.GetChildByIECLocation(parent)
       
   867             if module is None:
       
   868                 return "Error: No parent found"
       
   869             for child in _GetModuleChildren(module):
       
   870                 if child["name"] == name:
       
   871                     return "Error: A variable named %s already exists" % name
       
   872                 if child["location"] == location:
       
   873                     return "Error: A variable with location %s already exists" % ".".join(map(str, location))
       
   874             _GetLastModuleGroup(module).append({"name": name, 
       
   875                                                 "location": location, 
       
   876                                                 "type": LOCATION_TYPES[direction], 
       
   877                                                 "IEC_type": type, 
       
   878                                                 "description": description, 
       
   879                                                 "declare": dcode, 
       
   880                                                 "retrieve": rcode, 
       
   881                                                 "publish": pcode})
       
   882             self.RestartTimer()
       
   883 
       
   884         def ChangeVariableParams(self, parent, location, new_name, new_direction, new_type, new_dcode, new_rcode, new_pcode, new_description=None):
       
   885             module = self.PluginRoot.GetChildByIECLocation(parent)
       
   886             if module is None:
       
   887                 return "Error: No parent found"
       
   888             variable = None
       
   889             for child in _GetModuleChildren(module):
       
   890                 if child["location"] == location:
       
   891                     variable = child
       
   892                 elif child["name"] == new_name:
       
   893                     return "Error: A variable named %s already exists" % new_name
       
   894             if variable is None:
       
   895                 return "Error: No variable found"
       
   896             variable["name"] = new_name
       
   897             variable["type"] = LOCATION_TYPES[new_direction]
       
   898             variable["IEC_type"] = new_type
       
   899             variable["declare"] = new_dcode
       
   900             variable["retrieve"] = new_rcode
       
   901             variable["publish"] = new_pcode
       
   902             if new_description is not None:
       
   903                 variable["description"] = new_description
       
   904             self.RestartTimer()
       
   905     
       
   906         def RemoveVariable(self, parent, location):
       
   907             module = self.PluginRoot.GetChildByIECLocation(parent)
       
   908             if module is None:
       
   909                 return "Error: No parent found"
       
   910             child = _GetModuleVariable(module, location)
       
   911             if child is None:
       
   912                 return "Error: No variable found"
       
   913             _RemoveModuleChild(module, child)
       
   914             self.RestartTimer()
       
   915         
       
   916     def location(loc):
       
   917         return tuple(map(int, loc.split(".")))
       
   918     
       
   919     def GetCmdFunction(function, arg_types, opt=0):
       
   920         arg_number = len(arg_types)
       
   921         def CmdFunction(self, line):
       
   922             args_toks = line.split('"')
       
   923             if len(args_toks) % 2 == 0:
       
   924                 print "Error: Invalid command"
       
   925                 return
       
   926             args = []
       
   927             for num, arg in enumerate(args_toks):
       
   928                 if num % 2 == 0:
       
   929                     stripped = arg.strip()
       
   930                     if stripped:
       
   931                         args.extend(stripped.split(" "))
       
   932                 else:
       
   933                     args.append(arg)
       
   934             number = None
       
   935             extra = ""
       
   936             if opt == 0 and len(args) != arg_number:
       
   937                 number = arg_number
       
   938             elif len(args) > arg_number:
       
   939                 number = arg_number
       
   940                 extra = " at most"
       
   941             elif len(args) < arg_number - opt:
       
   942                 number = arg_number - opt
       
   943                 extra = " at least"
       
   944             if number is not None:
       
   945                 if number == 0:
       
   946                     print "Error: No argument%s expected" % extra
       
   947                 elif number == 1:
       
   948                     print "Error: 1 argument%s expected" % extra
       
   949                 else:
       
   950                     print "Error: %d arguments%s expected" % (number, extra)
       
   951                 return
       
   952             for num, arg in enumerate(args):
       
   953                 try:
       
   954                     args[num] = arg_types[num](arg)
       
   955                 except:
       
   956                     print "Error: Invalid value for argument %d" % (num + 1)
       
   957                     return
       
   958             res = getattr(self, function)(*args)
       
   959             if isinstance(res, (StringType, UnicodeType)):
       
   960                 print res
       
   961                 return False
       
   962             else:
       
   963                 return res
       
   964         return CmdFunction
       
   965     
       
   966     for function, (arg_types, opt) in {"Exit": ([], 0),
       
   967                                        "Show": ([], 0),
       
   968                                        "Refresh": ([], 0),
       
   969                                        "Close": ([], 0),
       
   970                                        "Compile": ([], 0),
       
   971                                        "SetProjectName": ([str], 0),
       
   972                                        "AddBus": ([int, str, str], 1),
       
   973                                        "RenameBus": ([int, str], 0),
       
   974                                        "ChangeBusIECChannel": ([int, int], 0),
       
   975                                        "RemoveBus": ([int], 0),
       
   976                                        "AddModule": ([location, int, str, str], 1), 
       
   977                                        "RenameModule": ([location, str], 0),
       
   978                                        "ChangeModuleIECChannel": ([location, int], 0),
       
   979                                        "RemoveModule": ([location, int], 0),
       
   980                                        "StartGroup": ([location, str, str], 1),
       
   981                                        "AddVariable": ([location, location, str, str, str, str, str, str], 1),
       
   982                                        "ChangeVariableParams": ([location, location, str, str, str, str, str, str, str, str], 1),
       
   983                                        "RemoveVariable": ([location, location], 0)}.iteritems():
       
   984         
       
   985         setattr(LPCBeremiz_Cmd, "do_%s" % function, GetCmdFunction(function, arg_types, opt))
       
   986     
       
   987     lpcberemiz_cmd = LPCBeremiz_Cmd(projectOpen, buildpath)
       
   988     lpcberemiz_cmd.cmdloop()
       
   989