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