plugger.py
changeset 14 eb9fdd316a40
parent 13 f1f0edbeb313
child 15 7a473efc4530
equal deleted inserted replaced
13:f1f0edbeb313 14:eb9fdd316a40
       
     1 """
       
     2 Base definitions for beremiz plugins
       
     3 """
       
     4 
     1 import os
     5 import os
       
     6 import types
       
     7 import shutil
       
     8 from xml.dom import minidom
     2 import plugins
     9 import plugins
     3 from plugins import PlugTemplate
    10 from xmlclass import GenerateClassesFromXSDstring
       
    11 
       
    12 _BaseParamsClass = GenerateClassesFromXSDstring("""<?xml version="1.0" encoding="ISO-8859-1" ?>
       
    13         <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       
    14           <xsd:element name="BaseParams">
       
    15             <xsd:complexType>
       
    16               <xsd:attribute name="Name" type="xsd:string" use="required"/>
       
    17               <xsd:attribute name="IEC_Channel" type="xsd:integer" use="required"  default="-1"/>
       
    18               <xsd:attribute name="Enabled" type="xsd:boolean" use="required" default="true"/>
       
    19             </xsd:complexType>
       
    20           </xsd:element>
       
    21         </xsd:schema>""")[0]["BaseParams"]
       
    22 
       
    23 NameTypeSeparator = '@'
       
    24 
       
    25 class PlugTemplate:
       
    26     """
       
    27     This class is the one that define plugins.
       
    28     """
       
    29 
       
    30     XSD = None
       
    31     PlugChildsTypes = []
       
    32     PlugMaxCount = None
       
    33     PluginMethods = []
       
    34 
       
    35     def _AddParamsMembers(self):
       
    36         Classes = GenerateClassesFromXSDstring(self.XSD)[0]
       
    37         self.PlugParams = []
       
    38         for name, XSDclass in Classes.items():
       
    39             if XSDclass.IsBaseClass:
       
    40                 obj = XSDclass()
       
    41                 self.PlugParams.append( (name, obj) )
       
    42                 setattr(self, name, obj)
       
    43 
       
    44     def __init__(self, PlugPath):
       
    45         # Create BaseParam 
       
    46         self.BaseParams = _BaseParamsClass()
       
    47         self.MandatoryParams = [("BaseParams", self.BaseParams)]
       
    48         self._AddParamsMembers()
       
    49         self.PluggedChilds = {}
       
    50     
       
    51     def PluginXmlFilePath(self, PlugName=None):
       
    52         return os.path.join(self.PlugPath(PlugName), "plugin.xml")
       
    53 
       
    54     def PlugPath(self,PlugName=None):
       
    55         if not PlugName:
       
    56             PlugName = self.BaseParams.getName()
       
    57         return os.path.join(self.PlugParent.PlugPath(), PlugName + NameTypeSeparator + self.PlugType)
       
    58     
       
    59     def PlugTestModified(self):
       
    60         return False
       
    61         
       
    62     def OnPlugSave(self):
       
    63         return True
       
    64 
       
    65     def PlugRequestSave(self):
       
    66         # If plugin do not have corresponding directory
       
    67         if not os.path.isdir(self.PlugPath(PlugName)):
       
    68             # Create it
       
    69             os.mkdir(self.PlugPath(PlugName))
       
    70 
       
    71         # generate XML for all XML parameters controllers of the plugin
       
    72         XMLString = '<?xml version="1.0" encoding="UTF-8"?>'
       
    73         for nodeName, XMLController in self.PlugParams + self.MandatoryParams:
       
    74             XMLString += XMLController.generateXMLTextMethod(self, nodeName, 0)
       
    75         XMLFile = open(self.PluginXmlFilePath(PlugName),'w')
       
    76         XMLFile.write(XMLString)
       
    77         XMLFile.close()
       
    78         
       
    79         # Call the plugin specific OnPlugSave method
       
    80         self.OnPlugSave()
       
    81         
       
    82         # go through all childs and do the same
       
    83         for PlugChild in self.IterChilds():
       
    84             PlugChild.PlugRequestSave()
       
    85     
       
    86     def PlugImport(self, src_PlugPath):
       
    87         shutil.copytree(src_PlugPath, self.PlugPath)
       
    88         return True
       
    89 
       
    90     def PlugGenerate_C(self, buildpath, current_location, locations):
       
    91         """
       
    92         Generate C code
       
    93         @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
       
    94         @param locations: List of complete variables locations \
       
    95             [(IEC_loc, IEC_Direction IEC_Type, Name)]\
       
    96             ex: [((0,0,4,5),'I','X','__IX_0_0_4_5'),...]
       
    97         """
       
    98         return [],""
       
    99     
       
   100     def _Generate_C(self, buildpath, current_location, locations):
       
   101         # Generate plugins [(Cfiles, CFLAGS)], LDFLAGS
       
   102         PlugCFilesAndCFLAGS, PlugLDFLAGS = self._Generate_C(buildpath, current_location, locations)
       
   103         # recurse through all childs, and stack their results
       
   104         for PlugChild in self.IterChilds():
       
   105             # Get childs [(Cfiles, CFLAGS)], LDFLAGS
       
   106             CFilesAndCFLAGS, LDFLAGS = \
       
   107                 PlugChild._Generate_C(
       
   108                     #keep the same path
       
   109                     buildpath,
       
   110                     # but update location (add curent IEC channel at the end)
       
   111                     current_location + (self.BaseParams.getIEC_Channel()),
       
   112                     # filter locations that start with current IEC location
       
   113                     [ (l,d,t,n) for l,d,t,n in locations if l[0:len(current_location)] == current_location ])
       
   114             # stack the result
       
   115             PlugCFilesAndCFLAGS += CFilesAndCFLAGS
       
   116             PlugLDFLAGS += LDFLAGS
       
   117         
       
   118         return PlugCFilesAndCFLAGS,PlugLDFLAGS
       
   119 
       
   120     def BlockTypesFactory(self):
       
   121         return []
       
   122 
       
   123     def STLibraryFactory(self):
       
   124         return ""
       
   125 
       
   126     def IterChilds(self):
       
   127         for PlugType, PluggedChilds in self.PluggedChilds.items():
       
   128             for PlugInstance in PluggedChilds:
       
   129                    yield PlugInstance
       
   130     
       
   131     def _GetChildBySomething(self, sep, something, matching):
       
   132         toks = matching.split(sep,1)
       
   133         for PlugInstance in self.IterChilds:
       
   134             # if match component of the name
       
   135             if getattr(PlugInstance.BaseParams, something) == toks[0]:
       
   136                 # if Name have other components
       
   137                 if len(toks) == 2:
       
   138                     # Recurse in order to find the latest object
       
   139                     return PlugInstance._GetChildBySomething( sep, something, toks[1])
       
   140                 # No sub name -> found
       
   141                 return PlugInstance
       
   142         # Not found
       
   143         return None
       
   144 
       
   145     def GetChildByName(self, Name):
       
   146         return self._GetChildBySomething('.',"Name", Name)
       
   147 
       
   148     def GetChildByIECLocation(self, Location):
       
   149         return self._GetChildBySomething('_',"IEC_Channel", Name)
       
   150     
       
   151     def FindNewIEC_Channel(self, DesiredChannel):
       
   152         """
       
   153         Changes IEC Channel number to DesiredChannel if available, nearest available if not.
       
   154         @param DesiredChannel: The desired IEC channel (int)
       
   155         """
       
   156         # Get Current IEC channel
       
   157         CurrentChannel = self.BaseParams.getIEC_Channel()
       
   158         # Do nothing if no change
       
   159         if CurrentChannel == DesiredChannel: return CurrentChannel
       
   160         # Build a list of used Channels out of parent's PluggedChilds
       
   161         AllChannels=[]
       
   162         for PlugInstance in self.PlugParent.IterChilds():
       
   163             if PlugInstance != self:
       
   164                 AllChannels.append(PlugInstance.BaseParams.getIEC_Channel())
       
   165         AllChannels.sort()
       
   166 
       
   167         # Now, try to guess the nearest available channel
       
   168         res = DesiredChannel
       
   169         while res in AllChannels: # While channel not free
       
   170             if res < CurrentChannel: # Want to go down ?
       
   171                 res -=  1 # Test for n-1
       
   172                 if res < 0 : return CurrentChannel # Can't go bellow 0, do nothing
       
   173             else : # Want to go up ?
       
   174                 res +=  1 # Test for n-1
       
   175         # Finally set IEC Channel
       
   176         self.BaseParams.setIEC_Channel(res)
       
   177         return res
       
   178 
       
   179     def OnPlugClose(self):
       
   180         return True
       
   181 
       
   182     def _doRemoveChild(self, PlugInstance):
       
   183         # Remove all childs of child
       
   184         for SubPlugInstance in PlugInstance.IterChilds():
       
   185             PlugInstance._doRemoveChild(SubPlugInstance)
       
   186         # Call the OnCloseMethod
       
   187         PlugInstance.OnPlugClose()
       
   188         # Delete plugin dir
       
   189         shutil.rmtree(PlugInstance.PlugPath())
       
   190         # Remove child of PluggedChilds
       
   191         self.PluggedChilds[PlugInstance.PlugType].remove(PlugInstance)
       
   192         # Forget it... (View have to refresh)
       
   193 
       
   194     def PlugRemoveChild(self, PlugName):
       
   195         # Fetch the plugin
       
   196         PlugInstance = self.GetChildByName(PlugName)
       
   197         # Ask to his parent to remove it
       
   198         PlugInstance.PlugParent._doRemoveChild(PlugInstance)
       
   199 
       
   200     def PlugAddChild(self, PlugName, PlugType):
       
   201         """
       
   202         Create the plugins that may be added as child to this node self
       
   203         @param PlugType: string desining the plugin class name (get name from PlugChildsTypes)
       
   204         @param PlugName: string for the name of the plugin instance
       
   205         """
       
   206         PlugChildsTypes = dict(self.PlugChildsTypes)
       
   207         # Check that adding this plugin is allowed
       
   208         try:
       
   209             PlugClass = PlugChildsTypes[PlugType]
       
   210         except KeyError:
       
   211             raise Exception, "Cannot create child %s of type %s "%(PlugName, PlugType)
       
   212         
       
   213         # if PlugClass is a class factory, call it. (prevent unneeded imports)
       
   214         if type(PlugClass) == types.FunctionType:
       
   215             PlugClass = PlugClass()
       
   216         
       
   217         # Eventualy Initialize child instance list for this class of plugin
       
   218         PluggedChildsWithSameClass = self.PluggedChilds.setdefault(PlugType,list())
       
   219         # Check count
       
   220         if PlugClass.MaxCount and len(PluggedChildsWithSameClass) >= PlugClass.MaxCount:
       
   221             raise Exception, "Max count (%d) reached for this plugin of type %s "%(PlugClass.MaxCount, PlugType)
       
   222         
       
   223         # create the final class, derived of provided plugin and template
       
   224         class FinalPlugClass(PlugClass, PlugTemplate):
       
   225             """
       
   226             Plugin class is derivated into FinalPlugClass before being instanciated
       
   227             This way __init__ is overloaded to ensure PlugTemplate.__init__ is called 
       
   228             before PlugClass.__init__, and to do the file related stuff.
       
   229             """
       
   230             def __init__(_self):
       
   231                 # self is the parent
       
   232                 _self.PlugParent = self
       
   233                 # Keep track of the plugin type name
       
   234                 _self.PlugType = PlugType
       
   235                 # Call the base plugin template init - change XSD into class members
       
   236                 PlugTemplate.__init__(_self)
       
   237                 # If dir have already be made, and file exist
       
   238                 if os.path.isdir(_self.PlugPath(PlugName)) and os.path.isfile(_self.PluginXmlFilePath(PlugName)):
       
   239                     #Load the plugin.xml file into parameters members
       
   240                     _self.LoadXMLParams()
       
   241                     # Call the plugin real __init__
       
   242                     PlugClass.__init__(_self)
       
   243                     #Load and init all the childs
       
   244                     _self.LoadChilds()
       
   245                     # Check that IEC_Channel is not already in use.
       
   246                     self.FindNewIEC_Channel(self.BaseParams.getIEC_Channel())
       
   247                 else:
       
   248                     # If plugin do not have corresponding file/dirs - they will be created on Save
       
   249                     # Set plugin name
       
   250                     _self.BaseParams.setName(PlugName)
       
   251                     # Find an IEC number
       
   252                     _self.FindNewIEC_Channel(0)
       
   253                     # Call the plugin real __init__
       
   254                     PlugClass.__init__(_self)
       
   255 
       
   256         # Create the object out of the resulting class
       
   257         newPluginOpj = FinalPlugClass()
       
   258         # Store it in PluggedChils
       
   259         PluggedChildsWithSameClass.append(newPluginOpj)
       
   260         
       
   261         return newPluginOpj
       
   262             
       
   263 
       
   264     def LoadXMLParams(self):
       
   265         # PlugParams have been filled, make a local dict to work with
       
   266         PlugParams = dict(self.PlugParams + self.MandatoryParams)
       
   267         # Get the xml tree
       
   268         xmlfile = open(self.PluginXmlFilePath(PlugName), 'r')
       
   269         tree = minidom.parse(xmlfile)
       
   270         xmlfile.close()
       
   271         # for each root elements
       
   272         for subtree in tree.childNodes:
       
   273             # if a plugin specific parameter set
       
   274             if subtree.nodeName in PlugParams:
       
   275                 #Load into associated xmlclass.
       
   276                 PlugParams[subtree.nodeName].loadXMLTree(subtree)
       
   277         
       
   278         # Basic check. Better to fail immediately.
       
   279         if(self.BaseParams.getName() != PlugName):
       
   280             raise Exception, "Project tree layout do not match plugin.xml %s!=%s "%(PlugName,self.BaseParams.getName())
       
   281         # Now, self.PlugPath() should be OK
       
   282 
       
   283     def LoadChilds(self):
       
   284         # Iterate over all PlugName@PlugType in plugin directory, and try to open them
       
   285         for PlugDir in os.listdir(self.PlugPath()):
       
   286             if os.path.isdir(os.path.join(self.PlugPath(),PlugDir)) and \
       
   287                PlugDir.count(NameTypeSeparator) == 1:
       
   288                 try:
       
   289                     self.PlugAddChild(*PlugDir.split[NameTypeSeparator])
       
   290                 except Exception, e:
       
   291                     print e
     4 
   292 
     5 
   293 
     6 class PluginsRoot(PlugTemplate):
   294 class PluginsRoot(PlugTemplate):
     7 
   295 
     8     # A special PlugChildsTypes
   296     # For root object, available Childs Types are modules of the plugin packages.
     9     PlugChildsTypes = [(name,lambda : getattr(__import__("plugins." + name), name).RootClass) for name in plugins.__all__]
   297     PlugChildsTypes = [(name,lambda : getattr(__import__("plugins." + name), name).RootClass) for name in plugins.__all__]
    10 
   298 
    11     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
   299     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
    12     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   300     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    13       <xsd:simpleType name="Win32Compiler">
   301       <xsd:simpleType name="Win32Compiler">
    28                     <xsd:attribute name="Priority" type="xsd:integer" use="required" default="0"/>
   316                     <xsd:attribute name="Priority" type="xsd:integer" use="required" default="0"/>
    29                   </xsd:complexType>
   317                   </xsd:complexType>
    30                 </xsd:element>
   318                 </xsd:element>
    31                 <xsd:element name="Linux">
   319                 <xsd:element name="Linux">
    32                   <xsd:complexType>
   320                   <xsd:complexType>
    33                     <xsd:attribute name="Compiler" type="xsd:string" use="required" default="0"/>
   321                     <xsd:attribute name="Compiler" type="xsd:string" use="required" default="gcc"/>
    34                     <xsd:attribute name="Nice" type="xsd:integer" use="required" default="0"/>
   322                     <xsd:attribute name="Nice" type="xsd:integer" use="required" default="0"/>
    35                   </xsd:complexType>
   323                   </xsd:complexType>
    36                 </xsd:element>
   324                 </xsd:element>
    37                 <xsd:element name="Xenomai">
   325                 <xsd:element name="Xenomai">
    38                   <xsd:complexType>
   326                   <xsd:complexType>
    39                     <xsd:attribute name="xeno-config" type="xsd:string" use="required" default="0"/>
   327                     <xsd:attribute name="xeno-config" type="xsd:string" use="required" default="/usr/xenomai/"/>
    40                     <xsd:attribute name="Compiler" type="xsd:string" use="required" default="0"/>
   328                     <xsd:attribute name="Compiler" type="xsd:string" use="required" default="0"/>
    41                     <xsd:attribute name="Priority" type="xsd:integer" use="required" default="0"/>
   329                     <xsd:attribute name="Priority" type="xsd:integer" use="required" default="0"/>
    42                   </xsd:complexType>
   330                   </xsd:complexType>
    43                 </xsd:element>
   331                 </xsd:element>
    44                 <xsd:element name="RTAI">
   332                 <xsd:element name="RTAI">