# HG changeset patch # User etisserant # Date 1188280656 -7200 # Node ID eb9fdd316a405da3b3ab6add3a0b09e55603e639 # Parent f1f0edbeb3139d5a3496e850247785380f2e541d More precise design for plugins.... to be continued... diff -r f1f0edbeb313 -r eb9fdd316a40 plugger.py --- a/plugger.py Mon Aug 27 17:54:55 2007 +0200 +++ b/plugger.py Tue Aug 28 07:57:36 2007 +0200 @@ -1,11 +1,299 @@ +""" +Base definitions for beremiz plugins +""" + import os +import types +import shutil +from xml.dom import minidom import plugins -from plugins import PlugTemplate +from xmlclass import GenerateClassesFromXSDstring + +_BaseParamsClass = GenerateClassesFromXSDstring(""" + + + + + + + + + """)[0]["BaseParams"] + +NameTypeSeparator = '@' + +class PlugTemplate: + """ + This class is the one that define plugins. + """ + + XSD = None + PlugChildsTypes = [] + PlugMaxCount = None + PluginMethods = [] + + def _AddParamsMembers(self): + Classes = GenerateClassesFromXSDstring(self.XSD)[0] + self.PlugParams = [] + for name, XSDclass in Classes.items(): + if XSDclass.IsBaseClass: + obj = XSDclass() + self.PlugParams.append( (name, obj) ) + setattr(self, name, obj) + + def __init__(self, PlugPath): + # Create BaseParam + self.BaseParams = _BaseParamsClass() + self.MandatoryParams = [("BaseParams", self.BaseParams)] + self._AddParamsMembers() + self.PluggedChilds = {} + + def PluginXmlFilePath(self, PlugName=None): + return os.path.join(self.PlugPath(PlugName), "plugin.xml") + + def PlugPath(self,PlugName=None): + if not PlugName: + PlugName = self.BaseParams.getName() + return os.path.join(self.PlugParent.PlugPath(), PlugName + NameTypeSeparator + self.PlugType) + + def PlugTestModified(self): + return False + + def OnPlugSave(self): + return True + + def PlugRequestSave(self): + # If plugin do not have corresponding directory + if not os.path.isdir(self.PlugPath(PlugName)): + # Create it + os.mkdir(self.PlugPath(PlugName)) + + # generate XML for all XML parameters controllers of the plugin + XMLString = '' + for nodeName, XMLController in self.PlugParams + self.MandatoryParams: + XMLString += XMLController.generateXMLTextMethod(self, nodeName, 0) + XMLFile = open(self.PluginXmlFilePath(PlugName),'w') + XMLFile.write(XMLString) + XMLFile.close() + + # Call the plugin specific OnPlugSave method + self.OnPlugSave() + + # go through all childs and do the same + for PlugChild in self.IterChilds(): + PlugChild.PlugRequestSave() + + def PlugImport(self, src_PlugPath): + shutil.copytree(src_PlugPath, self.PlugPath) + return True + + def PlugGenerate_C(self, buildpath, current_location, locations): + """ + Generate C code + @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5) + @param locations: List of complete variables locations \ + [(IEC_loc, IEC_Direction IEC_Type, Name)]\ + ex: [((0,0,4,5),'I','X','__IX_0_0_4_5'),...] + """ + return [],"" + + def _Generate_C(self, buildpath, current_location, locations): + # Generate plugins [(Cfiles, CFLAGS)], LDFLAGS + PlugCFilesAndCFLAGS, PlugLDFLAGS = self._Generate_C(buildpath, current_location, locations) + # recurse through all childs, and stack their results + for PlugChild in self.IterChilds(): + # Get childs [(Cfiles, CFLAGS)], LDFLAGS + CFilesAndCFLAGS, LDFLAGS = \ + PlugChild._Generate_C( + #keep the same path + buildpath, + # but update location (add curent IEC channel at the end) + current_location + (self.BaseParams.getIEC_Channel()), + # filter locations that start with current IEC location + [ (l,d,t,n) for l,d,t,n in locations if l[0:len(current_location)] == current_location ]) + # stack the result + PlugCFilesAndCFLAGS += CFilesAndCFLAGS + PlugLDFLAGS += LDFLAGS + + return PlugCFilesAndCFLAGS,PlugLDFLAGS + + def BlockTypesFactory(self): + return [] + + def STLibraryFactory(self): + return "" + + def IterChilds(self): + for PlugType, PluggedChilds in self.PluggedChilds.items(): + for PlugInstance in PluggedChilds: + yield PlugInstance + + def _GetChildBySomething(self, sep, something, matching): + toks = matching.split(sep,1) + for PlugInstance in self.IterChilds: + # if match component of the name + if getattr(PlugInstance.BaseParams, something) == toks[0]: + # if Name have other components + if len(toks) == 2: + # Recurse in order to find the latest object + return PlugInstance._GetChildBySomething( sep, something, toks[1]) + # No sub name -> found + return PlugInstance + # Not found + return None + + def GetChildByName(self, Name): + return self._GetChildBySomething('.',"Name", Name) + + def GetChildByIECLocation(self, Location): + return self._GetChildBySomething('_',"IEC_Channel", Name) + + def FindNewIEC_Channel(self, DesiredChannel): + """ + Changes IEC Channel number to DesiredChannel if available, nearest available if not. + @param DesiredChannel: The desired IEC channel (int) + """ + # Get Current IEC channel + CurrentChannel = self.BaseParams.getIEC_Channel() + # Do nothing if no change + if CurrentChannel == DesiredChannel: return CurrentChannel + # Build a list of used Channels out of parent's PluggedChilds + AllChannels=[] + for PlugInstance in self.PlugParent.IterChilds(): + if PlugInstance != self: + AllChannels.append(PlugInstance.BaseParams.getIEC_Channel()) + AllChannels.sort() + + # Now, try to guess the nearest available channel + res = DesiredChannel + while res in AllChannels: # While channel not free + if res < CurrentChannel: # Want to go down ? + res -= 1 # Test for n-1 + if res < 0 : return CurrentChannel # Can't go bellow 0, do nothing + else : # Want to go up ? + res += 1 # Test for n-1 + # Finally set IEC Channel + self.BaseParams.setIEC_Channel(res) + return res + + def OnPlugClose(self): + return True + + def _doRemoveChild(self, PlugInstance): + # Remove all childs of child + for SubPlugInstance in PlugInstance.IterChilds(): + PlugInstance._doRemoveChild(SubPlugInstance) + # Call the OnCloseMethod + PlugInstance.OnPlugClose() + # Delete plugin dir + shutil.rmtree(PlugInstance.PlugPath()) + # Remove child of PluggedChilds + self.PluggedChilds[PlugInstance.PlugType].remove(PlugInstance) + # Forget it... (View have to refresh) + + def PlugRemoveChild(self, PlugName): + # Fetch the plugin + PlugInstance = self.GetChildByName(PlugName) + # Ask to his parent to remove it + PlugInstance.PlugParent._doRemoveChild(PlugInstance) + + def PlugAddChild(self, PlugName, PlugType): + """ + Create the plugins that may be added as child to this node self + @param PlugType: string desining the plugin class name (get name from PlugChildsTypes) + @param PlugName: string for the name of the plugin instance + """ + PlugChildsTypes = dict(self.PlugChildsTypes) + # Check that adding this plugin is allowed + try: + PlugClass = PlugChildsTypes[PlugType] + except KeyError: + raise Exception, "Cannot create child %s of type %s "%(PlugName, PlugType) + + # if PlugClass is a class factory, call it. (prevent unneeded imports) + if type(PlugClass) == types.FunctionType: + PlugClass = PlugClass() + + # Eventualy Initialize child instance list for this class of plugin + PluggedChildsWithSameClass = self.PluggedChilds.setdefault(PlugType,list()) + # Check count + if PlugClass.MaxCount and len(PluggedChildsWithSameClass) >= PlugClass.MaxCount: + raise Exception, "Max count (%d) reached for this plugin of type %s "%(PlugClass.MaxCount, PlugType) + + # create the final class, derived of provided plugin and template + class FinalPlugClass(PlugClass, PlugTemplate): + """ + Plugin class is derivated into FinalPlugClass before being instanciated + This way __init__ is overloaded to ensure PlugTemplate.__init__ is called + before PlugClass.__init__, and to do the file related stuff. + """ + def __init__(_self): + # self is the parent + _self.PlugParent = self + # Keep track of the plugin type name + _self.PlugType = PlugType + # Call the base plugin template init - change XSD into class members + PlugTemplate.__init__(_self) + # If dir have already be made, and file exist + if os.path.isdir(_self.PlugPath(PlugName)) and os.path.isfile(_self.PluginXmlFilePath(PlugName)): + #Load the plugin.xml file into parameters members + _self.LoadXMLParams() + # Call the plugin real __init__ + PlugClass.__init__(_self) + #Load and init all the childs + _self.LoadChilds() + # Check that IEC_Channel is not already in use. + self.FindNewIEC_Channel(self.BaseParams.getIEC_Channel()) + else: + # If plugin do not have corresponding file/dirs - they will be created on Save + # Set plugin name + _self.BaseParams.setName(PlugName) + # Find an IEC number + _self.FindNewIEC_Channel(0) + # Call the plugin real __init__ + PlugClass.__init__(_self) + + # Create the object out of the resulting class + newPluginOpj = FinalPlugClass() + # Store it in PluggedChils + PluggedChildsWithSameClass.append(newPluginOpj) + + return newPluginOpj + + + def LoadXMLParams(self): + # PlugParams have been filled, make a local dict to work with + PlugParams = dict(self.PlugParams + self.MandatoryParams) + # Get the xml tree + xmlfile = open(self.PluginXmlFilePath(PlugName), 'r') + tree = minidom.parse(xmlfile) + xmlfile.close() + # for each root elements + for subtree in tree.childNodes: + # if a plugin specific parameter set + if subtree.nodeName in PlugParams: + #Load into associated xmlclass. + PlugParams[subtree.nodeName].loadXMLTree(subtree) + + # Basic check. Better to fail immediately. + if(self.BaseParams.getName() != PlugName): + raise Exception, "Project tree layout do not match plugin.xml %s!=%s "%(PlugName,self.BaseParams.getName()) + # Now, self.PlugPath() should be OK + + def LoadChilds(self): + # Iterate over all PlugName@PlugType in plugin directory, and try to open them + for PlugDir in os.listdir(self.PlugPath()): + if os.path.isdir(os.path.join(self.PlugPath(),PlugDir)) and \ + PlugDir.count(NameTypeSeparator) == 1: + try: + self.PlugAddChild(*PlugDir.split[NameTypeSeparator]) + except Exception, e: + print e class PluginsRoot(PlugTemplate): - # A special PlugChildsTypes + # For root object, available Childs Types are modules of the plugin packages. PlugChildsTypes = [(name,lambda : getattr(__import__("plugins." + name), name).RootClass) for name in plugins.__all__] XSD = """ @@ -30,13 +318,13 @@ - + - + diff -r f1f0edbeb313 -r eb9fdd316a40 plugins/__init__.py --- a/plugins/__init__.py Mon Aug 27 17:54:55 2007 +0200 +++ b/plugins/__init__.py Tue Aug 28 07:57:36 2007 +0200 @@ -4,7 +4,3 @@ _base_path = path.split(__file__)[0] __all__ = [name for name in listdir(_base_path) if path.isdir(path.join(_base_path, name)) and name != "CVS" or name.endswith(".py") and not name.startswith("__")] - -#for name in __all__: -# __import__(name, globals(), locals(), []) - diff -r f1f0edbeb313 -r eb9fdd316a40 plugins/__templates.py --- a/plugins/__templates.py Mon Aug 27 17:54:55 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,289 +0,0 @@ -""" -Base definitions for beremiz plugins -""" - -import os -import types -import shutil -from xml.dom import minidom - -_BaseParamsClass = GenerateClassesFromXSDstring(""" - - - - - - - - - """)[0]["BaseParams"] - -NameTypeSeparator = '@' - -class PlugTemplate: - """ - This class is the one that define plugins. - """ - - XSD = None - PlugChildsTypes = [] - PlugMaxCount = None - PluginMethods = [] - - def _AddParamsMembers(self): - Classes = GenerateClassesFromXSDstring(self.XSD)[0] - self.PlugParams = [] - for name, XSDclass in Classes.items(): - if XSDclass.IsBaseClass: - obj = XSDclass() - self.PlugParams.append( (name, obj) ) - setattr(self, name, obj) - - def __init__(self, PlugPath): - # Create BaseParam - self.BaseParams = _BaseParamsClass() - self.MandatoryParams = [("BaseParams", self.BaseParams)] - self._AddParamsMembers() - self.PluggedChilds = {} - - def PluginXmlFilePath(self, PlugName=None): - return os.path.join(self.PlugPath(PlugName), "plugin.xml") - - def PlugPath(self,PlugName=None): - if not PlugName: - PlugName = self.BaseParams.getName() - return os.path.join(self.PlugParent.PlugPath(), PlugName + NameTypeSeparator + self.PlugType) - - def PlugTestModified(self): - return False - - def OnPlugSave(self): - return True - - def PlugRequestSave(self): - # If plugin do not have corresponding directory - if not os.path.isdir(self.PlugPath(PlugName)): - # Create it - os.mkdir(self.PlugPath(PlugName)) - - # generate XML for all XML parameters controllers of the plugin - XMLString = '' - for nodeName, XMLController in self.PlugParams + self.MandatoryParams: - XMLString += XMLController.generateXMLTextMethod(self, nodeName, 0) - XMLFile = open(self.PluginXmlFilePath(PlugName),'w') - XMLFile.write(XMLString) - XMLFile.close() - - # Call the plugin specific OnPlugSave method - self.OnPlugSave() - - # go through all childs and do the same - for PlugChild in self.IterChilds(): - PlugChild.PlugRequestSave() - - def PlugImport(self, src_PlugPath): - shutil.copytree(src_PlugPath, self.PlugPath) - return True - - def PlugGenerate_C(self, buildpath, current_location, locations): - """ - Generate C code - @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5) - @param locations: List of complete variables locations \ - [(IEC_loc, IEC_Direction IEC_Type, Name)]\ - ex: [((0,0,4,5),'I','X','__IX_0_0_4_5'),...] - """ - return [],"" - - def _Generate_C(self, buildpath, current_location, locations): - # Generate plugins [(Cfiles, CFLAGS)], LDFLAGS - PlugCFilesAndCFLAGS, PlugLDFLAGS = self._Generate_C(buildpath, current_location, locations) - # recurse through all childs, and stack their results - for PlugChild in self.IterChilds(): - # Get childs [(Cfiles, CFLAGS)], LDFLAGS - CFilesAndCFLAGS, LDFLAGS = \ - PlugChild._Generate_C( - #keep the same path - buildpath, - # but update location (add curent IEC channel at the end) - current_location + (self.BaseParams.getIEC_Channel()), - # filter locations that start with current IEC location - [ (l,d,t,n) for l,d,t,n in locations if l[0:len(current_location)] == current_location ]) - # stack the result - PlugCFilesAndCFLAGS += CFilesAndCFLAGS - PlugLDFLAGS += LDFLAGS - - return PlugCFilesAndCFLAGS,PlugLDFLAGS - - def BlockTypesFactory(self): - return [] - - def STLibraryFactory(self): - return "" - - def IterChilds(self): - for PlugType, PluggedChilds in self.PluggedChilds.items(): - for PlugInstance in PluggedChilds: - yield PlugInstance - - def _GetChildBySomething(self, sep, something, matching): - toks = matching.split(sep,1) - for PlugInstance in self.IterChilds: - # if match component of the name - if getattr(PlugInstance.BaseParams, something) == toks[0]: - # if Name have other components - if len(toks) == 2: - # Recurse in order to find the latest object - return PlugInstance._GetChildBySomething( sep, something, toks[1]) - # No sub name -> found - return PlugInstance - # Not found - return None - - def GetChildByName(self, Name): - return self._GetChildBySomething('.',"Name", Name) - - def GetChildByIECLocation(self, Location): - return self._GetChildBySomething('_',"IEC_Channel", Name) - - def FindNewIEC_Channel(self, DesiredChannel): - """ - Changes IEC Channel number to DesiredChannel if available, nearest available if not. - @param DesiredChannel: The desired IEC channel (int) - """ - # Get Current IEC channel - CurrentChannel = self.BaseParams.getIEC_Channel() - # Do nothing if no change - if CurrentChannel == DesiredChannel: return CurrentChannel - # Build a list of used Channels out of parent's PluggedChilds - AllChannels=[] - for PlugInstance in self.PlugParent.IterChilds(): - if PlugInstance != self: - AllChannels.append(PlugInstance.BaseParams.getIEC_Channel()) - AllChannels.sort() - - # Now, try to guess the nearest available channel - res = DesiredChannel - while res in AllChannels: # While channel not free - if res < CurrentChannel: # Want to go down ? - res -= 1 # Test for n-1 - if res < 0 : return CurrentChannel # Can't go bellow 0, do nothing - else : # Want to go up ? - res += 1 # Test for n-1 - # Finally set IEC Channel - self.BaseParams.setIEC_Channel(res) - return res - - def OnPlugClose(self): - return True - - def _doRemoveChild(self, PlugInstance): - # Remove all childs of child - for SubPlugInstance in PlugInstance.IterChilds(): - PlugInstance._doRemoveChild(SubPlugInstance) - # Call the OnCloseMethod - PlugInstance.OnPlugClose() - # Delete plugin dir - shutil.rmtree(PlugInstance.PlugPath()) - # Remove child of PluggedChilds - self.PluggedChilds[PlugInstance.PlugType].remove(PlugInstance) - # Forget it... (View have to refresh) - - def PlugRemoveChild(self, PlugName): - # Fetch the plugin - PlugInstance = self.GetChildByName(PlugName) - # Ask to his parent to remove it - PlugInstance.PlugParent._doRemoveChild(PlugInstance) - - def PlugAddChild(self, PlugName, PlugType): - """ - Create the plugins that may be added as child to this node self - @param PlugType: string desining the plugin class name (get name from PlugChildsTypes) - @param PlugName: string for the name of the plugin instance - """ - PlugChildsTypes = dict(self.PlugChildsTypes) - # Check that adding this plugin is allowed - try: - PlugClass = PlugChildsTypes[PlugType] - except KeyError: - raise Exception, "Cannot create child %s of type %s "%(PlugName, PlugType) - - # if PlugClass is a class factory, call it. (prevent unneeded imports) - if type(PlugClass) == types.FunctionType: - PlugClass = PlugClass() - - # Eventualy Initialize child instance list for this class of plugin - PluggedChildsWithSameClass = self.PluggedChilds.setdefault(PlugType,list()) - # Check count - if PlugClass.MaxCount and len(PluggedChildsWithSameClass) >= PlugClass.MaxCount: - raise Exception, "Max count (%d) reached for this plugin of type %s "%(PlugClass.MaxCount, PlugType) - - # create the final class, derived of provided plugin and template - class FinalPlugClass(PlugClass, PlugTemplate): - """ - Plugin class is derivated into FinalPlugClass before being instanciated - This way __init__ is overloaded to ensure PlugTemplate.__init__ is called - before PlugClass.__init__, and to do the file related stuff. - """ - def __init__(_self): - # self is the parent - _self.PlugParent = self - # Keep track of the plugin type name - _self.PlugType = PlugType - # Call the base plugin template init - change XSD into class members - PlugTemplate.__init__(_self) - # If dir have already be made, and file exist - if os.path.isdir(_self.PlugPath(PlugName)) and os.path.isfile(_self.PluginXmlFilePath(PlugName)): - #Load the plugin.xml file into parameters members - _self.LoadXMLParams() - # Call the plugin real __init__ - PlugClass.__init__(_self) - #Load and init all the childs - _self.LoadChilds() - # Check that IEC_Channel is not already in use. - self.FindNewIEC_Channel(self.BaseParams.getIEC_Channel()) - else: - # If plugin do not have corresponding file/dirs - they will be created on Save - # Set plugin name - _self.BaseParams.setName(PlugName) - # Find an IEC number - _self.FindNewIEC_Channel(0) - # Call the plugin real __init__ - PlugClass.__init__(_self) - - # Create the object out of the resulting class - newPluginOpj = FinalPlugClass() - # Store it in PluggedChils - PluggedChildsWithSameClass.append(newPluginOpj) - - return newPluginOpj - - - def LoadXMLParams(self): - # PlugParams have been filled, make a local dict to work with - PlugParams = dict(self.PlugParams + self.MandatoryParams) - # Get the xml tree - xmlfile = open(self.PluginXmlFilePath(PlugName), 'r') - tree = minidom.parse(xmlfile) - xmlfile.close() - # for each root elements - for subtree in tree.childNodes: - # if a plugin specific parameter set - if subtree.nodeName in PlugParams: - #Load into associated xmlclass. - PlugParams[subtree.nodeName].loadXMLTree(subtree) - - # Basic check. Better to fail immediately. - if(self.BaseParams.getName() != PlugName): - raise Exception, "Project tree layout do not match plugin.xml %s!=%s "%(PlugName,self.BaseParams.getName()) - # Now, self.PlugPath() should be OK - - def LoadChilds(self): - # Iterate over all PlugName@PlugType in plugin directory, and try to open them - for PlugDir in os.listdir(self.PlugPath()): - if os.path.isdir(os.path.join(self.PlugPath(),PlugDir)) and \ - PlugDir.count(NameTypeSeparator) == 1: - try: - self.PlugAddChild(*PlugDir.split[NameTypeSeparator]) - except Exception, e: - print e