|
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"> |