115 # |
116 # |
116 # This means that objects of class _BacnetSlavePlug may safely access all the members |
117 # This means that objects of class _BacnetSlavePlug may safely access all the members |
117 # of classes ConfigTreeNode as well as FinalCTNClass (since they are always instantiated |
118 # of classes ConfigTreeNode as well as FinalCTNClass (since they are always instantiated |
118 # as a FinalCTNClass) |
119 # as a FinalCTNClass) |
119 EditorType = BacnetSlaveEditorPlug |
120 EditorType = BacnetSlaveEditorPlug |
120 |
121 |
121 # The following classes follow the model/viewer design pattern |
122 # The following classes follow the model/viewer design pattern |
122 # |
123 # |
123 # _BacnetSlavePlug - contains the model (i.e. configuration parameters) |
124 # _BacnetSlavePlug - contains the model (i.e. configuration parameters) |
124 # BacnetSlaveEditorPlug - contains the viewer (and editor, so it includes the 'controller' part of the |
125 # BacnetSlaveEditorPlug - contains the viewer (and editor, so it includes the 'controller' part of the |
125 # design pattern which in this case is not separated from the viewer) |
126 # design pattern which in this case is not separated from the viewer) |
126 # |
127 # |
127 # The _BacnetSlavePlug object is 'permanent', i.e. it exists as long as the beremiz project is open |
128 # The _BacnetSlavePlug object is 'permanent', i.e. it exists as long as the beremiz project is open |
128 # The BacnetSlaveEditorPlug object is 'transient', i.e. it exists only while the editor is visible/open |
129 # The BacnetSlaveEditorPlug object is 'transient', i.e. it exists only while the editor is visible/open |
129 # in the editing panel. It is destoryed whenever |
130 # in the editing panel. It is destoryed whenever |
130 # the user closes the corresponding tab in the |
131 # the user closes the corresponding tab in the |
131 # editing panel, and a new object is created when |
132 # editing panel, and a new object is created when |
132 # the editor is re-opened. |
133 # the editor is re-opened. |
133 # |
134 # |
134 # _BacnetSlavePlug contains: AV_ObjTable, ... |
135 # _BacnetSlavePlug contains: AV_ObjTable, ... |
135 # (these are the objects that actually store the config parameters or 'model' |
136 # (these are the objects that actually store the config parameters or 'model' |
136 # and are therefore stored to a file) |
137 # and are therefore stored to a file) |
137 # |
138 # |
138 # _BacnetSlavePlug contains: AV_VarEditor, ... |
139 # _BacnetSlavePlug contains: AV_VarEditor, ... |
139 # (these are the objects that implement a grid table to edit/view the |
140 # (these are the objects that implement a grid table to edit/view the |
140 # corresponding mode parameters) |
141 # corresponding mode parameters) |
141 # |
142 # |
142 # Logic: |
143 # Logic: |
143 # - The xx_VarEditor classes inherit from wx.grid.Grid |
144 # - The xx_VarEditor classes inherit from wx.grid.Grid |
144 # - The xx_ObjTable classes inherit from wx.grid.PyGridTableBase |
145 # - The xx_ObjTable classes inherit from wx.grid.PyGridTableBase |
145 # To be more precise, the inheritance tree is actually: |
146 # To be more precise, the inheritance tree is actually: |
146 # xx_VarEditor -> ObjectGrid -> CustomGrid -> wx.grid.Grid |
147 # xx_VarEditor -> ObjectGrid -> CustomGrid -> wx.grid.Grid |
147 # xx_ObjTable -> ObjectTable -> CustomTable -> wx.grid.PyGridTableBase) |
148 # xx_ObjTable -> ObjectTable -> CustomTable -> wx.grid.PyGridTableBase) |
148 # |
149 # |
149 # Note that wx.grid.Grid is prepared to work with wx.grid.PyGridTableBase as the container of |
150 # Note that wx.grid.Grid is prepared to work with wx.grid.PyGridTableBase as the container of |
150 # data that is displayed and edited in the Grid. |
151 # data that is displayed and edited in the Grid. |
151 |
152 |
152 |
|
153 ConfNodeMethods = [ |
153 ConfNodeMethods = [ |
154 {"bitmap" : "ExportSlave", |
154 {"bitmap": "ExportSlave", |
155 "name" : _("Export slave"), |
155 "name": _("Export slave"), |
156 "tooltip" : _("Export BACnet slave to EDE file"), |
156 "tooltip": _("Export BACnet slave to EDE file"), |
157 "method" : "_ExportBacnetSlave"}, |
157 "method": "_ExportBacnetSlave"}, |
158 ] |
158 ] |
159 |
159 |
160 def __init__(self): |
160 def __init__(self): |
161 # Initialize the dictionary that stores the current configuration for the Analog/Digital/MultiValued Variables |
161 # Initialize the dictionary that stores the current configuration for the Analog/Digital/MultiValued Variables |
162 # in this BACnet server. |
162 # in this BACnet server. |
163 self.ObjTablesData = {} |
163 self.ObjTablesData = {} |
164 self.ObjTablesData[ "AV_Obj"] = [] # Each list will contain an entry for each row in the xxxxVar grid!! |
164 |
165 self.ObjTablesData[ "AO_Obj"] = [] # Each entry/row will be a dictionary |
165 # Each list will contain an entry for each row in the xxxxVar grid!! |
166 self.ObjTablesData[ "AI_Obj"] = [] # Each dictionary will contain all entries/data |
166 # Each entry/row will be a dictionary |
167 self.ObjTablesData[ "BV_Obj"] = [] # for one row in the grid. |
167 # Each dictionary will contain all entries/data |
168 self.ObjTablesData[ "BO_Obj"] = [] # Same structure as explained above... |
168 # for one row in the grid. |
169 self.ObjTablesData[ "BI_Obj"] = [] # Same structure as explained above... |
169 |
170 self.ObjTablesData["MSV_Obj"] = [] # Same structure as explained above... |
170 self.ObjTablesData["AV_Obj"] = [] |
171 self.ObjTablesData["MSO_Obj"] = [] # Same structure as explained above... |
171 self.ObjTablesData["AO_Obj"] = [] |
172 self.ObjTablesData["MSI_Obj"] = [] # Same structure as explained above... |
172 self.ObjTablesData["AI_Obj"] = [] |
173 |
173 self.ObjTablesData["BV_Obj"] = [] |
174 self.ObjTablesData["EDEfile_parm"] = {"next_EDE_file_version":1} |
174 self.ObjTablesData["BO_Obj"] = [] |
175 # EDE files inlcude extra parameters (ex. file version) |
175 self.ObjTablesData["BI_Obj"] = [] |
176 # We would like to save the parameters the user configures |
176 self.ObjTablesData["MSV_Obj"] = [] |
177 # so they are available the next time the user opens the project. |
177 self.ObjTablesData["MSO_Obj"] = [] |
178 # Since this plugin is only storing the ObjTablesData[] dict |
178 self.ObjTablesData["MSI_Obj"] = [] |
179 # to file, we add that info to this dictionary too. |
179 |
180 # Yes, I know this is kind of a hack. |
180 self.ObjTablesData["EDEfile_parm"] = {"next_EDE_file_version": 1} |
181 |
181 |
|
182 # EDE files inlcude extra parameters (ex. file version) |
|
183 # We would like to save the parameters the user configures |
|
184 # so they are available the next time the user opens the project. |
|
185 # Since this plugin is only storing the ObjTablesData[] dict |
|
186 # to file, we add that info to this dictionary too. |
|
187 # Yes, I know this is kind of a |
|
188 # hack. |
|
189 |
182 filepath = self.GetFileName() |
190 filepath = self.GetFileName() |
183 if(os.path.isfile(filepath)): |
191 if os.path.isfile(filepath): |
184 self.LoadFromFile(filepath) |
192 self.LoadFromFile(filepath) |
185 |
193 |
186 self.ObjTables = {} |
194 self.ObjTables = {} |
187 self.ObjTables[ "AV_Obj"] = ObjectTable(self, self.ObjTablesData[ "AV_Obj"], AVObject) |
195 self.ObjTables["AV_Obj"] = ObjectTable( |
188 self.ObjTables[ "AO_Obj"] = ObjectTable(self, self.ObjTablesData[ "AO_Obj"], AOObject) |
196 self, self.ObjTablesData["AV_Obj"], AVObject) |
189 self.ObjTables[ "AI_Obj"] = ObjectTable(self, self.ObjTablesData[ "AI_Obj"], AIObject) |
197 self.ObjTables["AO_Obj"] = ObjectTable( |
190 self.ObjTables[ "BV_Obj"] = ObjectTable(self, self.ObjTablesData[ "BV_Obj"], BVObject) |
198 self, self.ObjTablesData["AO_Obj"], AOObject) |
191 self.ObjTables[ "BO_Obj"] = ObjectTable(self, self.ObjTablesData[ "BO_Obj"], BOObject) |
199 self.ObjTables["AI_Obj"] = ObjectTable( |
192 self.ObjTables[ "BI_Obj"] = ObjectTable(self, self.ObjTablesData[ "BI_Obj"], BIObject) |
200 self, self.ObjTablesData["AI_Obj"], AIObject) |
193 self.ObjTables["MSV_Obj"] = ObjectTable(self, self.ObjTablesData["MSV_Obj"], MSVObject) |
201 self.ObjTables["BV_Obj"] = ObjectTable( |
194 self.ObjTables["MSO_Obj"] = ObjectTable(self, self.ObjTablesData["MSO_Obj"], MSOObject) |
202 self, self.ObjTablesData["BV_Obj"], BVObject) |
195 self.ObjTables["MSI_Obj"] = ObjectTable(self, self.ObjTablesData["MSI_Obj"], MSIObject) |
203 self.ObjTables["BO_Obj"] = ObjectTable( |
196 # list containing the data in the table <--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
204 self, self.ObjTablesData["BO_Obj"], BOObject) |
197 |
205 self.ObjTables["BI_Obj"] = ObjectTable( |
198 |
206 self, self.ObjTablesData["BI_Obj"], BIObject) |
199 ###################################### |
207 self.ObjTables["MSV_Obj"] = ObjectTable( |
|
208 self, self.ObjTablesData["MSV_Obj"], MSVObject) |
|
209 self.ObjTables["MSO_Obj"] = ObjectTable( |
|
210 self, self.ObjTablesData["MSO_Obj"], MSOObject) |
|
211 self.ObjTables["MSI_Obj"] = ObjectTable( |
|
212 self, self.ObjTablesData["MSI_Obj"], MSIObject) |
|
213 |
|
214 # |
200 # Functions to be called by CTNClass # |
215 # Functions to be called by CTNClass # |
201 ###################################### |
216 # |
202 # The following functions would be somewhat equvalent to virtual functions/methods in C++ classes |
217 # The following functions would be somewhat equvalent to virtual functions/methods in C++ classes |
203 # They will be called by the base class (CTNClass) from which this _BacnetSlavePlug class derives. |
218 # They will be called by the base class (CTNClass) from which this |
204 |
219 # _BacnetSlavePlug class derives. |
|
220 |
205 def GetCurrentNodeName(self): |
221 def GetCurrentNodeName(self): |
206 return self.CTNName() |
222 return self.CTNName() |
207 |
223 |
208 def GetFileName(self): |
224 def GetFileName(self): |
209 return os.path.join(self.CTNPath(), 'bacnet_slave') |
225 return os.path.join(self.CTNPath(), 'bacnet_slave') |
210 |
226 |
211 def OnCTNSave(self, from_project_path=None): |
227 def OnCTNSave(self, from_project_path=None): |
212 return self.SaveToFile(self.GetFileName()) |
228 return self.SaveToFile(self.GetFileName()) |
213 |
|
214 |
229 |
215 def CTNTestModified(self): |
230 def CTNTestModified(self): |
216 # self.ChangesToSave: Check whether any of the parameters, defined in the XSD above, were changed. |
231 # self.ChangesToSave: Check whether any of the parameters, defined in the XSD above, were changed. |
217 # This is handled by the ConfigTreeNode class |
232 # This is handled by the ConfigTreeNode class |
218 # (Remember that no objects are ever instantiated from _BacnetSlavePlug. |
233 # (Remember that no objects are ever instantiated from _BacnetSlavePlug. |
219 # Objects are instead created from FinalCTNClass, which derives from |
234 # Objects are instead created from FinalCTNClass, which derives from |
220 # _BacnetSlavePlug and ConfigTreeNode. This means that we can exceptionally |
235 # _BacnetSlavePlug and ConfigTreeNode. This means that we can exceptionally |
221 # consider that all objects of type _BacnetSlavePlug will also be a ConfigTreeNode). |
236 # consider that all objects of type _BacnetSlavePlug will also be a |
222 result = self.ChangesToSave or self.ObjTables[ "AV_Obj"].ChangesToSave \ |
237 # ConfigTreeNode). |
223 or self.ObjTables[ "AO_Obj"].ChangesToSave \ |
238 result = self.ChangesToSave \ |
224 or self.ObjTables[ "AI_Obj"].ChangesToSave \ |
239 or self.ObjTables["AV_Obj"].ChangesToSave \ |
225 or self.ObjTables[ "BV_Obj"].ChangesToSave \ |
240 or self.ObjTables["AO_Obj"].ChangesToSave \ |
226 or self.ObjTables[ "BO_Obj"].ChangesToSave \ |
241 or self.ObjTables["AI_Obj"].ChangesToSave \ |
227 or self.ObjTables[ "BI_Obj"].ChangesToSave \ |
242 or self.ObjTables["BV_Obj"].ChangesToSave \ |
228 or self.ObjTables["MSV_Obj"].ChangesToSave \ |
243 or self.ObjTables["BO_Obj"].ChangesToSave \ |
229 or self.ObjTables["MSO_Obj"].ChangesToSave \ |
244 or self.ObjTables["BI_Obj"].ChangesToSave \ |
230 or self.ObjTables["MSI_Obj"].ChangesToSave |
245 or self.ObjTables["MSV_Obj"].ChangesToSave \ |
|
246 or self.ObjTables["MSO_Obj"].ChangesToSave \ |
|
247 or self.ObjTables["MSI_Obj"].ChangesToSave |
231 return result |
248 return result |
232 |
249 |
233 ### Currently not needed. Override _OpenView() in case we need to do some special stuff whenever the editor is opened! |
250 # Currently not needed. Override _OpenView() in case we need to do some special stuff whenever the editor is opened! |
234 ##def _OpenView(self, name=None, onlyopened=False): |
251 # def _OpenView(self, name=None, onlyopened=False): |
235 ##print "_BacnetSlavePlug._OpenView() Called!!!" |
252 # print "_BacnetSlavePlug._OpenView() Called!!!" |
236 ##ConfigTreeNode._OpenView(self, name, onlyopened) |
253 # ConfigTreeNode._OpenView(self, name, onlyopened) |
237 ###print self._View |
254 # print self._View |
238 #####if self._View is not None: |
255 # if self._View is not None: |
239 #####self._View.SetBusId(self.GetCurrentLocation()) |
256 # self._View.SetBusId(self.GetCurrentLocation()) |
240 ##return self._View |
257 # return self._View |
241 |
|
242 |
258 |
243 def GetVariableLocationTree(self): |
259 def GetVariableLocationTree(self): |
244 current_location = self.GetCurrentLocation() |
260 current_location = self.GetCurrentLocation() |
245 # see comment in CTNGenerate_C regarding identical line of code! |
261 # see comment in CTNGenerate_C regarding identical line of code! |
246 locstr = ".".join(map(str,current_location)) |
262 locstr = ".".join(map(str, current_location)) |
247 |
263 |
248 # IDs used by BACnet to identify object types/class. |
264 # IDs used by BACnet to identify object types/class. |
249 # OBJECT_ANALOG_INPUT = 0, |
265 # OBJECT_ANALOG_INPUT = 0, |
250 # OBJECT_ANALOG_OUTPUT = 1, |
266 # OBJECT_ANALOG_OUTPUT = 1, |
251 # OBJECT_ANALOG_VALUE = 2, |
267 # OBJECT_ANALOG_VALUE = 2, |
252 # OBJECT_BINARY_INPUT = 3, |
268 # OBJECT_BINARY_INPUT = 3, |
268 # etc.. |
284 # etc.. |
269 # |
285 # |
270 # Value objects will be mapped onto %M |
286 # Value objects will be mapped onto %M |
271 # Input objects will be mapped onto %I |
287 # Input objects will be mapped onto %I |
272 # Output objects will be mapped onto %Q |
288 # Output objects will be mapped onto %Q |
273 |
289 |
274 BACnetEntries = [] |
290 BACnetEntries = [] |
275 BACnetEntries.append(self.GetSlaveLocationTree( |
291 BACnetEntries.append(self.GetSlaveLocationTree( |
276 self.ObjTablesData[ "AV_Obj"], 32, 'REAL', 'D', locstr+ '.2', 'Analog Values')) |
292 self.ObjTablesData["AV_Obj"], 32, 'REAL', 'D', locstr + '.2', 'Analog Values')) |
277 BACnetEntries.append(self.GetSlaveLocationTree( |
293 BACnetEntries.append(self.GetSlaveLocationTree( |
278 self.ObjTablesData[ "AO_Obj"], 32, 'REAL', 'D', locstr+ '.1', 'Analog Outputs')) |
294 self.ObjTablesData["AO_Obj"], 32, 'REAL', 'D', locstr + '.1', 'Analog Outputs')) |
279 BACnetEntries.append(self.GetSlaveLocationTree( |
295 BACnetEntries.append(self.GetSlaveLocationTree( |
280 self.ObjTablesData[ "AI_Obj"], 32, 'REAL', 'D', locstr+ '.0', 'Analog Inputs')) |
296 self.ObjTablesData["AI_Obj"], 32, 'REAL', 'D', locstr + '.0', 'Analog Inputs')) |
281 BACnetEntries.append(self.GetSlaveLocationTree( |
297 BACnetEntries.append(self.GetSlaveLocationTree( |
282 self.ObjTablesData[ "BV_Obj"], 1, 'BOOL', 'X', locstr+ '.5', 'Binary Values')) |
298 self.ObjTablesData["BV_Obj"], 1, 'BOOL', 'X', locstr + '.5', 'Binary Values')) |
283 BACnetEntries.append(self.GetSlaveLocationTree( |
299 BACnetEntries.append(self.GetSlaveLocationTree( |
284 self.ObjTablesData[ "BO_Obj"], 1, 'BOOL', 'X', locstr+ '.4', 'Binary Outputs')) |
300 self.ObjTablesData["BO_Obj"], 1, 'BOOL', 'X', locstr + '.4', 'Binary Outputs')) |
285 BACnetEntries.append(self.GetSlaveLocationTree( |
301 BACnetEntries.append(self.GetSlaveLocationTree( |
286 self.ObjTablesData[ "BI_Obj"], 1, 'BOOL', 'X', locstr+ '.3', 'Binary Inputs')) |
302 self.ObjTablesData["BI_Obj"], 1, 'BOOL', 'X', locstr + '.3', 'Binary Inputs')) |
287 BACnetEntries.append(self.GetSlaveLocationTree( |
303 BACnetEntries.append(self.GetSlaveLocationTree( |
288 self.ObjTablesData["MSV_Obj"], 8, 'BYTE', 'B', locstr+'.19', 'Multi State Values')) |
304 self.ObjTablesData["MSV_Obj"], 8, 'BYTE', 'B', locstr + '.19', 'Multi State Values')) |
289 BACnetEntries.append(self.GetSlaveLocationTree( |
305 BACnetEntries.append(self.GetSlaveLocationTree( |
290 self.ObjTablesData["MSO_Obj"], 8, 'BYTE', 'B', locstr+'.14', 'Multi State Outputs')) |
306 self.ObjTablesData["MSO_Obj"], 8, 'BYTE', 'B', locstr + '.14', 'Multi State Outputs')) |
291 BACnetEntries.append(self.GetSlaveLocationTree( |
307 BACnetEntries.append(self.GetSlaveLocationTree( |
292 self.ObjTablesData["MSI_Obj"], 8, 'BYTE', 'B', locstr+'.13', 'Multi State Inputs')) |
308 self.ObjTablesData["MSI_Obj"], 8, 'BYTE', 'B', locstr + '.13', 'Multi State Inputs')) |
293 |
309 |
294 return {"name": self.BaseParams.getName(), |
310 return {"name": self.BaseParams.getName(), |
295 "type": LOCATION_CONFNODE, |
311 "type": LOCATION_CONFNODE, |
296 "location": locstr + ".x", |
312 "location": locstr + ".x", |
297 "children": BACnetEntries} |
313 "children": BACnetEntries} |
298 |
314 |
299 |
315 # |
300 ############################ |
|
301 # Helper functions/methods # |
316 # Helper functions/methods # |
302 ############################ |
317 # |
303 # a helper function to GetVariableLocationTree() |
318 # a helper function to GetVariableLocationTree() |
304 def GetSlaveLocationTree(self, ObjTablesData, size_in_bits, IECdatatype, location_size, location_str, name): |
319 def GetSlaveLocationTree(self, ObjTablesData, size_in_bits, IECdatatype, location_size, location_str, name): |
305 BACnetObjectEntries = [] |
320 BACnetObjectEntries = [] |
306 for xx_ObjProp in ObjTablesData: |
321 for xx_ObjProp in ObjTablesData: |
307 BACnetObjectEntries.append({ |
322 BACnetObjectEntries.append({ |
308 "name": str(xx_ObjProp["Object Identifier"]) + ': ' + xx_ObjProp["Object Name"], |
323 "name": str(xx_ObjProp["Object Identifier"]) + ': ' + xx_ObjProp["Object Name"], |
309 "type": LOCATION_VAR_MEMORY, # LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, or LOCATION_VAR_MEMORY |
324 "type": LOCATION_VAR_MEMORY, # LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, or LOCATION_VAR_MEMORY |
310 "size": size_in_bits, # 1 or 16 |
325 "size": size_in_bits, # 1 or 16 |
311 "IEC_type": IECdatatype, # 'BOOL', 'WORD', ... |
326 "IEC_type": IECdatatype, # 'BOOL', 'WORD', ... |
312 "var_name": "var_name", # seems to be ignored?? |
327 "var_name": "var_name", # seems to be ignored?? |
313 "location": location_size + location_str + "." + str(xx_ObjProp["Object Identifier"]), |
328 "location": location_size + location_str + "." + str(xx_ObjProp["Object Identifier"]), |
314 "description": "description", # seems to be ignored? |
329 "description": "description", # seems to be ignored? |
315 "children": []}) |
330 "children": []}) |
316 |
331 |
317 BACnetEntries = [] |
332 return {"name": name, |
318 return {"name": name, |
333 "type": LOCATION_CONFNODE, |
319 "type": LOCATION_CONFNODE, |
334 "location": location_str + ".x", |
320 "location": location_str + ".x", |
335 "children": BACnetObjectEntries} |
321 "children": BACnetObjectEntries} |
336 |
322 |
|
323 |
|
324 # Returns a dictionary with: |
337 # Returns a dictionary with: |
325 # keys: names of BACnet objects |
338 # keys: names of BACnet objects |
326 # value: number of BACnet objects using this same name |
339 # value: number of BACnet objects using this same name |
327 # (values larger than 1 indicates an error as BACnet requires unique names) |
340 # (values larger than 1 indicates an error as BACnet requires unique names) |
328 def GetObjectNamesCount(self): |
341 def GetObjectNamesCount(self): |
329 # The dictionary is built by first creating a list containing the names of _ALL_ |
342 # The dictionary is built by first creating a list containing the names of _ALL_ |
330 # BACnet objects currently configured by the user (using the GUI) |
343 # BACnet objects currently configured by the user (using the GUI) |
331 ObjectNames = [] |
344 ObjectNames = [] |
332 ObjectNames.extend(self.ObjTables[ "AV_Obj"].GetAllValuesByName("Object Name")) |
345 ObjectNames.extend( |
333 ObjectNames.extend(self.ObjTables[ "AO_Obj"].GetAllValuesByName("Object Name")) |
346 self.ObjTables["AV_Obj"].GetAllValuesByName("Object Name")) |
334 ObjectNames.extend(self.ObjTables[ "AI_Obj"].GetAllValuesByName("Object Name")) |
347 ObjectNames.extend( |
335 ObjectNames.extend(self.ObjTables[ "BV_Obj"].GetAllValuesByName("Object Name")) |
348 self.ObjTables["AO_Obj"].GetAllValuesByName("Object Name")) |
336 ObjectNames.extend(self.ObjTables[ "BO_Obj"].GetAllValuesByName("Object Name")) |
349 ObjectNames.extend( |
337 ObjectNames.extend(self.ObjTables[ "BI_Obj"].GetAllValuesByName("Object Name")) |
350 self.ObjTables["AI_Obj"].GetAllValuesByName("Object Name")) |
338 ObjectNames.extend(self.ObjTables["MSV_Obj"].GetAllValuesByName("Object Name")) |
351 ObjectNames.extend( |
339 ObjectNames.extend(self.ObjTables["MSO_Obj"].GetAllValuesByName("Object Name")) |
352 self.ObjTables["BV_Obj"].GetAllValuesByName("Object Name")) |
340 ObjectNames.extend(self.ObjTables["MSI_Obj"].GetAllValuesByName("Object Name")) |
353 ObjectNames.extend( |
|
354 self.ObjTables["BO_Obj"].GetAllValuesByName("Object Name")) |
|
355 ObjectNames.extend( |
|
356 self.ObjTables["BI_Obj"].GetAllValuesByName("Object Name")) |
|
357 ObjectNames.extend( |
|
358 self.ObjTables["MSV_Obj"].GetAllValuesByName("Object Name")) |
|
359 ObjectNames.extend( |
|
360 self.ObjTables["MSO_Obj"].GetAllValuesByName("Object Name")) |
|
361 ObjectNames.extend( |
|
362 self.ObjTables["MSI_Obj"].GetAllValuesByName("Object Name")) |
341 # This list is then transformed into a collections.Counter class |
363 # This list is then transformed into a collections.Counter class |
342 # Which is then transformed into a dictionary using dict() |
364 # Which is then transformed into a dictionary using dict() |
343 return dict(Counter(ObjectNames)) |
365 return dict(Counter(ObjectNames)) |
344 |
366 |
345 # Check whether the current configuration contains BACnet objects configured |
367 # Check whether the current configuration contains BACnet objects configured |
346 # with the same identical object name (returns True or False) |
368 # with the same identical object name (returns True or False) |
347 def HasDuplicateObjectNames(self): |
369 def HasDuplicateObjectNames(self): |
348 ObjectNamesCount = self.GetObjectNamesCount() |
370 ObjectNamesCount = self.GetObjectNamesCount() |
349 for ObjName in ObjectNamesCount: |
371 for ObjName in ObjectNamesCount: |
352 return False |
374 return False |
353 |
375 |
354 # Check whether any object ID is used more than once (not valid in BACnet) |
376 # Check whether any object ID is used more than once (not valid in BACnet) |
355 # (returns True or False) |
377 # (returns True or False) |
356 def HasDuplicateObjectIDs(self): |
378 def HasDuplicateObjectIDs(self): |
357 res = self.ObjTables[ "AV_Obj"].HasDuplicateObjectIDs() |
379 res = self.ObjTables["AV_Obj"].HasDuplicateObjectIDs() |
358 res = res or self.ObjTables[ "AO_Obj"].HasDuplicateObjectIDs() |
380 res = res or self.ObjTables["AO_Obj"].HasDuplicateObjectIDs() |
359 res = res or self.ObjTables[ "AI_Obj"].HasDuplicateObjectIDs() |
381 res = res or self.ObjTables["AI_Obj"].HasDuplicateObjectIDs() |
360 res = res or self.ObjTables[ "BV_Obj"].HasDuplicateObjectIDs() |
382 res = res or self.ObjTables["BV_Obj"].HasDuplicateObjectIDs() |
361 res = res or self.ObjTables[ "BO_Obj"].HasDuplicateObjectIDs() |
383 res = res or self.ObjTables["BO_Obj"].HasDuplicateObjectIDs() |
362 res = res or self.ObjTables[ "BI_Obj"].HasDuplicateObjectIDs() |
384 res = res or self.ObjTables["BI_Obj"].HasDuplicateObjectIDs() |
363 res = res or self.ObjTables["MSV_Obj"].HasDuplicateObjectIDs() |
385 res = res or self.ObjTables["MSV_Obj"].HasDuplicateObjectIDs() |
364 res = res or self.ObjTables["MSO_Obj"].HasDuplicateObjectIDs() |
386 res = res or self.ObjTables["MSO_Obj"].HasDuplicateObjectIDs() |
365 res = res or self.ObjTables["MSI_Obj"].HasDuplicateObjectIDs() |
387 res = res or self.ObjTables["MSI_Obj"].HasDuplicateObjectIDs() |
366 return res |
388 return res |
367 |
389 |
368 |
390 # |
369 ####################################################### |
|
370 # Methods related to files (saving/loading/exporting) # |
391 # Methods related to files (saving/loading/exporting) # |
371 ####################################################### |
392 # |
372 def SaveToFile(self, filepath): |
393 def SaveToFile(self, filepath): |
373 # Save node data in file |
394 # Save node data in file |
374 # The configuration data declared in the XSD string will be saved by the ConfigTreeNode class, |
395 # The configuration data declared in the XSD string will be saved by the ConfigTreeNode class, |
375 # so we only need to save the data that is stored in ObjTablesData objects |
396 # so we only need to save the data that is stored in ObjTablesData objects |
376 # Note that we do not store the ObjTables objects. ObjTables is of a class that |
397 # Note that we do not store the ObjTables objects. ObjTables is of a class that |
377 # contains more stuff we do not need to store. Actually it is a bad idea to store |
398 # contains more stuff we do not need to store. Actually it is a bad idea to store |
378 # this extra stuff (as we would make the files we generate dependent on the actual |
399 # this extra stuff (as we would make the files we generate dependent on the actual |
379 # version of the wx library we are using!!! Remember that ObjTables evetually |
400 # version of the wx library we are using!!! Remember that ObjTables evetually |
380 # derives/inherits from wx.grid.PyGridTableBase). Another reason not to store the whole |
401 # derives/inherits from wx.grid.PyGridTableBase). Another reason not to store the whole |
381 # object is because it is not pickable (i.e. pickle.dump() cannot handle it)!! |
402 # object is because it is not pickable (i.e. pickle.dump() cannot handle it)!! |
382 try: |
403 try: |
383 fd = open(filepath, "w") |
404 fd = open(filepath, "w") |
384 pickle.dump(self.ObjTablesData, fd) |
405 pickle.dump(self.ObjTablesData, fd) |
385 fd.close() |
406 fd.close() |
386 # On successfull save, reset flags to indicate no more changes that need saving |
407 # On successfull save, reset flags to indicate no more changes that |
387 self.ObjTables[ "AV_Obj"].ChangesToSave = False |
408 # need saving |
388 self.ObjTables[ "AO_Obj"].ChangesToSave = False |
409 self.ObjTables["AV_Obj"].ChangesToSave = False |
389 self.ObjTables[ "AI_Obj"].ChangesToSave = False |
410 self.ObjTables["AO_Obj"].ChangesToSave = False |
390 self.ObjTables[ "BV_Obj"].ChangesToSave = False |
411 self.ObjTables["AI_Obj"].ChangesToSave = False |
391 self.ObjTables[ "BO_Obj"].ChangesToSave = False |
412 self.ObjTables["BV_Obj"].ChangesToSave = False |
392 self.ObjTables[ "BI_Obj"].ChangesToSave = False |
413 self.ObjTables["BO_Obj"].ChangesToSave = False |
|
414 self.ObjTables["BI_Obj"].ChangesToSave = False |
393 self.ObjTables["MSV_Obj"].ChangesToSave = False |
415 self.ObjTables["MSV_Obj"].ChangesToSave = False |
394 self.ObjTables["MSO_Obj"].ChangesToSave = False |
416 self.ObjTables["MSO_Obj"].ChangesToSave = False |
395 self.ObjTables["MSI_Obj"].ChangesToSave = False |
417 self.ObjTables["MSI_Obj"].ChangesToSave = False |
396 return True |
418 return True |
397 except: |
419 except: |
398 return _("Unable to save to file \"%s\"!")%filepath |
420 return _("Unable to save to file \"%s\"!") % filepath |
399 |
421 |
400 def LoadFromFile(self, filepath): |
422 def LoadFromFile(self, filepath): |
401 # Load the data that is saved in SaveToFile() |
423 # Load the data that is saved in SaveToFile() |
402 try: |
424 try: |
403 fd = open(filepath, "r") |
425 fd = open(filepath, "r") |
404 self.ObjTablesData = pickle.load(fd) |
426 self.ObjTablesData = pickle.load(fd) |
405 fd.close() |
427 fd.close() |
406 return True |
428 return True |
407 except: |
429 except: |
408 return _("Unable to load file \"%s\"!")%filepath |
430 return _("Unable to load file \"%s\"!") % filepath |
409 |
431 |
410 def _ExportBacnetSlave(self): |
432 def _ExportBacnetSlave(self): |
411 dialog = wx.FileDialog(self.GetCTRoot().AppFrame, |
433 dialog = wx.FileDialog(self.GetCTRoot().AppFrame, |
412 _("Choose a file"), |
434 _("Choose a file"), |
413 os.path.expanduser("~"), |
435 os.path.expanduser("~"), |
414 "%s_EDE.csv" % self.CTNName(), |
436 "%s_EDE.csv" % self.CTNName(), |
415 _("EDE files (*_EDE.csv)|*_EDE.csv|All files|*.*"), |
437 _("EDE files (*_EDE.csv)|*_EDE.csv|All files|*.*"), |
416 wx.SAVE|wx.OVERWRITE_PROMPT) |
438 wx.SAVE | wx.OVERWRITE_PROMPT) |
417 if dialog.ShowModal() == wx.ID_OK: |
439 if dialog.ShowModal() == wx.ID_OK: |
418 result = self.GenerateEDEFile(dialog.GetPath()) |
440 result = self.GenerateEDEFile(dialog.GetPath()) |
419 result = False |
441 result = False |
420 if result: |
442 if result: |
421 self.GetCTRoot().logger.write_error(_("Error: Export slave failed\n")) |
443 self.GetCTRoot().logger.write_error( |
422 dialog.Destroy() |
444 _("Error: Export slave failed\n")) |
423 |
445 dialog.Destroy() |
424 |
446 |
425 def GenerateEDEFile(self, filename): |
447 def GenerateEDEFile(self, filename): |
426 template_file_dir = os.path.join(os.path.split(__file__)[0],"ede_files") |
448 template_file_dir = os.path.join( |
427 |
449 os.path.split(__file__)[0], "ede_files") |
428 #The BACnetServerNode attribute is added dynamically by ConfigTreeNode._AddParamsMembers() |
450 |
429 # It will be an XML parser object created by GenerateParserFromXSDstring(self.XSD).CreateRoot() |
451 # The BACnetServerNode attribute is added dynamically by ConfigTreeNode._AddParamsMembers() |
|
452 # It will be an XML parser object created by |
|
453 # GenerateParserFromXSDstring(self.XSD).CreateRoot() |
430 BACnet_Device_ID = self.BACnetServerNode.getBACnet_Device_ID() |
454 BACnet_Device_ID = self.BACnetServerNode.getBACnet_Device_ID() |
431 |
455 |
432 # The EDE file contains a header that includes general project data (name, author, ...) |
456 # The EDE file contains a header that includes general project data (name, author, ...) |
433 # Instead of asking the user for this data, we get it from the configuration |
457 # Instead of asking the user for this data, we get it from the configuration |
434 # of the Beremiz project itself. |
458 # of the Beremiz project itself. |
435 # We ask the root Config Tree Node for the data... |
459 # We ask the root Config Tree Node for the data... |
436 ProjProp = {} |
460 ProjProp = {} |
437 FileProp = {} |
461 FileProp = {} |
438 CTN_Root = self.GetCTRoot() # this should be an object of class ProjectController |
462 |
439 Project = CTN_Root.Project # this should be an object capable of parsing |
463 # this should be an object of class ProjectController |
440 # PLCopen XML files. The parser is created automatically |
464 CTN_Root = self.GetCTRoot() |
441 # (i.e. using GenerateParserFromXSD() from xmlclass module) |
465 |
442 # using the PLCopen XSD file defining the format of the XML. |
466 # this should be an object capable of parsing |
443 # See the file plcopen/plcopen.py |
467 # PLCopen XML files. The parser is created automatically |
|
468 # (i.e. using GenerateParserFromXSD() from xmlclass module) |
|
469 # using the PLCopen XSD file defining the format of the XML. |
|
470 # See the file plcopen/plcopen.py |
|
471 Project = CTN_Root.Project |
444 if Project is not None: |
472 if Project is not None: |
445 # getcontentHeader() and getfileHeader() are functions that are conditionally defined in |
473 # getcontentHeader() and getfileHeader() are functions that are conditionally defined in |
446 # plcopn/plcopen.py We cannot rely on their existance |
474 # plcopn/plcopen.py We cannot rely on their existance |
447 if getattr(Project, "getcontentHeader", None) is not None: |
475 if getattr(Project, "getcontentHeader", None) is not None: |
448 ProjProp = Project.getcontentHeader() |
476 ProjProp = Project.getcontentHeader() |
449 # getcontentHeader() returns a dictionary. Available keys are: |
477 # getcontentHeader() returns a dictionary. Available keys are: |
450 # "projectName", "projectVersion", "modificationDateTime", |
478 # "projectName", "projectVersion", "modificationDateTime", |
451 # "organization", "authorName", "language", "pageSize", "scaling" |
479 # "organization", "authorName", "language", "pageSize", "scaling" |
452 if getattr(Project, "getfileHeader", None) is not None: |
480 if getattr(Project, "getfileHeader", None) is not None: |
453 FileProp = Project.getfileHeader() |
481 FileProp = Project.getfileHeader() |
454 # getfileHeader() returns a dictionary. Available keys are: |
482 # getfileHeader() returns a dictionary. Available keys are: |
455 # "companyName", "companyURL", "productName", "productVersion", |
483 # "companyName", "companyURL", "productName", "productVersion", |
456 # "productRelease", "creationDateTime", "contentDescription" |
484 # "productRelease", "creationDateTime", "contentDescription" |
457 |
485 |
458 ProjName = "" |
486 ProjName = "" |
459 if "projectName" in ProjProp: |
487 if "projectName" in ProjProp: |
460 ProjName = ProjProp["projectName"] |
488 ProjName = ProjProp["projectName"] |
461 ProjAuthor = "" |
489 ProjAuthor = "" |
462 if "companyName" in FileProp: |
490 if "companyName" in FileProp: |
463 ProjAuthor += "(" + FileProp["companyName"] + ")" |
491 ProjAuthor += "(" + FileProp["companyName"] + ")" |
464 if "authorName" in ProjProp: |
492 if "authorName" in ProjProp: |
465 ProjAuthor = ProjProp["authorName"] + " " + ProjAuthor |
493 ProjAuthor = ProjProp["authorName"] + " " + ProjAuthor |
466 |
494 |
467 projdata_dict = {} |
495 projdata_dict = {} |
468 projdata_dict["Project Name"] = ProjName |
496 projdata_dict["Project Name"] = ProjName |
469 projdata_dict["Project Author"] = ProjAuthor |
497 projdata_dict["Project Author"] = ProjAuthor |
470 projdata_dict["Current Time"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') |
498 projdata_dict["Current Time"] = datetime.now().strftime( |
471 projdata_dict["EDE file version"] = self.ObjTablesData["EDEfile_parm"]["next_EDE_file_version"] |
499 '%Y-%m-%d %H:%M:%S') |
|
500 projdata_dict["EDE file version"] = self.ObjTablesData[ |
|
501 "EDEfile_parm"]["next_EDE_file_version"] |
472 |
502 |
473 # Next time we generate an EDE file, use another version! |
503 # Next time we generate an EDE file, use another version! |
474 self.ObjTablesData["EDEfile_parm"]["next_EDE_file_version"] += 1 |
504 self.ObjTablesData["EDEfile_parm"]["next_EDE_file_version"] += 1 |
475 |
505 |
476 AX_params_format = "%(Object Name)s;" + str(BACnet_Device_ID) + ";%(Object Name)s;%(BACnetObjTypeID)s;%(Object Identifier)s;%(Description)s;0;;;%(Settable)s;N;;;;%(Unit ID)s;" |
506 AX_params_format = "%(Object Name)s;" + str(BACnet_Device_ID) + \ |
477 |
507 ";%(Object Name)s;%(BACnetObjTypeID)s;%(Object Identifier)s;%(Description)s;0;;;%(Settable)s;N;;;;%(Unit ID)s;" |
478 BX_params_format = "%(Object Name)s;" + str(BACnet_Device_ID) + ";%(Object Name)s;%(BACnetObjTypeID)s;%(Object Identifier)s;%(Description)s;0;0;1;%(Settable)s;N;;;;;" |
508 |
479 |
509 BX_params_format = "%(Object Name)s;" + str(BACnet_Device_ID) + \ |
480 MSX_params_format = "%(Object Name)s;" + str(BACnet_Device_ID) + ";%(Object Name)s;%(BACnetObjTypeID)s;%(Object Identifier)s;%(Description)s;1;1;%(Number of States)s;%(Settable)s;N;;;;;" |
510 ";%(Object Name)s;%(BACnetObjTypeID)s;%(Object Identifier)s;%(Description)s;0;0;1;%(Settable)s;N;;;;;" |
481 |
511 |
|
512 MSX_params_format = "%(Object Name)s;" + str(BACnet_Device_ID) + \ |
|
513 ";%(Object Name)s;%(BACnetObjTypeID)s;%(Object Identifier)s;%(Description)s;1;1;%(Number of States)s;%(Settable)s;N;;;;;" |
|
514 |
482 Objects_List = [] |
515 Objects_List = [] |
483 for ObjType, params_format in [ |
516 for ObjType, params_format in [("AV", AX_params_format), |
484 ("AV" , AX_params_format ), ("AO" , AX_params_format ), ("AI" , AX_params_format ), |
517 ("AO", AX_params_format), |
485 ("BV" , BX_params_format ), ("BO" , BX_params_format ), ("BI" , BX_params_format ), |
518 ("AI", AX_params_format), |
486 ("MSV", MSX_params_format ), ("MSO", MSX_params_format ), ("MSI", MSX_params_format ) |
519 ("BV", BX_params_format), |
487 ]: |
520 ("BO", BX_params_format), |
|
521 ("BI", BX_params_format), |
|
522 ("MSV", MSX_params_format), |
|
523 ("MSO", MSX_params_format), |
|
524 ("MSI", MSX_params_format)]: |
488 self.ObjTables[ObjType + "_Obj"].UpdateAllVirtualProperties() |
525 self.ObjTables[ObjType + "_Obj"].UpdateAllVirtualProperties() |
489 for ObjProp in self.ObjTablesData[ObjType + "_Obj"]: |
526 for ObjProp in self.ObjTablesData[ObjType + "_Obj"]: |
490 Objects_List.append(params_format % ObjProp) |
527 Objects_List.append(params_format % ObjProp) |
491 |
528 |
492 # Normalize filename |
529 # Normalize filename |
493 for extension in ["_EDE.csv", "_ObjTypes.csv", "_StateTexts.csv", "_Units.csv"]: |
530 for extension in ["_EDE.csv", "_ObjTypes.csv", "_StateTexts.csv", "_Units.csv"]: |
494 if filename.lower().endswith(extension.lower()): |
531 if filename.lower().endswith(extension.lower()): |
495 filename = filename[:-len(extension)] |
532 filename = filename[:-len(extension)] |
496 |
533 |
497 # EDE_header |
534 # EDE_header |
498 generate_file_name = filename + "_EDE.csv" |
535 generate_file_name = filename + "_EDE.csv" |
499 template_file_name = os.path.join(template_file_dir,"template_EDE.csv") |
536 template_file_name = os.path.join( |
|
537 template_file_dir, "template_EDE.csv") |
500 generate_file_content = open(template_file_name).read() % projdata_dict |
538 generate_file_content = open(template_file_name).read() % projdata_dict |
501 generate_file_handle = open(generate_file_name,'w') |
539 generate_file_handle = open(generate_file_name, 'w') |
502 generate_file_handle .write(generate_file_content) |
540 generate_file_handle .write(generate_file_content) |
503 generate_file_handle .write("\n".join(Objects_List)) |
541 generate_file_handle .write("\n".join(Objects_List)) |
504 generate_file_handle .close() |
542 generate_file_handle .close() |
505 |
543 |
506 # templates of remaining files do not need changes. They are simply copied unchanged! |
544 # templates of remaining files do not need changes. They are simply |
|
545 # copied unchanged! |
507 for extension in ["_ObjTypes.csv", "_StateTexts.csv", "_Units.csv"]: |
546 for extension in ["_ObjTypes.csv", "_StateTexts.csv", "_Units.csv"]: |
508 generate_file_name = filename + extension |
547 generate_file_name = filename + extension |
509 template_file_name = os.path.join(template_file_dir,"template" + extension) |
548 template_file_name = os.path.join( |
|
549 template_file_dir, "template" + extension) |
510 generate_file_content = open(template_file_name).read() |
550 generate_file_content = open(template_file_name).read() |
511 generate_file_handle = open(generate_file_name,'w') |
551 generate_file_handle = open(generate_file_name, 'w') |
512 generate_file_handle .write(generate_file_content) |
552 generate_file_handle .write(generate_file_content) |
513 generate_file_handle .close() |
553 generate_file_handle .close() |
514 |
554 |
515 |
555 # |
516 ############################# |
|
517 # Generate the source files # |
556 # Generate the source files # |
518 ############################# |
557 # |
519 def CTNGenerate_C(self, buildpath, locations): |
558 def CTNGenerate_C(self, buildpath, locations): |
520 # Determine the current location in Beremiz's project configuration tree |
559 # Determine the current location in Beremiz's project configuration |
|
560 # tree |
521 current_location = self.GetCurrentLocation() |
561 current_location = self.GetCurrentLocation() |
522 # The current location of this plugin in Beremiz's configuration tree, separated by underscores |
562 # The current location of this plugin in Beremiz's configuration tree, separated by underscores |
523 # NOTE: Since BACnet plugin currently does not use sub-branches in the tree (in other words, this |
563 # NOTE: Since BACnet plugin currently does not use sub-branches in the tree (in other words, this |
524 # _BacnetSlavePlug class was actually renamed as the RootClass), the current_location_dots |
564 # _BacnetSlavePlug class was actually renamed as the RootClass), the current_location_dots |
525 # will actually be a single number (e.g.: 0 or 3 or 6, corresponding to the location |
565 # will actually be a single number (e.g.: 0 or 3 or 6, corresponding to the location |
526 # in which the plugin was inserted in the Beremiz configuration tree on Beremiz's left panel). |
566 # in which the plugin was inserted in the Beremiz configuration tree on Beremiz's left panel). |
527 locstr = "_".join(map(str,current_location)) |
567 locstr = "_".join(map(str, current_location)) |
528 |
568 |
529 # First check whether all the current parameters (inserted by user in the GUI) are valid... |
569 # First check whether all the current parameters (inserted by user in |
|
570 # the GUI) are valid... |
530 if self.HasDuplicateObjectNames(): |
571 if self.HasDuplicateObjectNames(): |
531 self.GetCTRoot().logger.write_warning(_("Error: BACnet server '%s.x: %s' contains objects with duplicate object names.\n")%(locstr, self.CTNName())) |
572 self.GetCTRoot().logger.write_warning( |
532 raise Exception, False |
573 _("Error: BACnet server '%s.x: %s' contains objects with duplicate object names.\n") % (locstr, self.CTNName())) |
533 # TODO: return an error code instead of raising an exception (currently unsupported by Beremiz) |
574 raise Exception(False) |
|
575 # TODO: return an error code instead of raising an exception |
|
576 # (currently unsupported by Beremiz) |
534 |
577 |
535 if self.HasDuplicateObjectIDs(): |
578 if self.HasDuplicateObjectIDs(): |
536 self.GetCTRoot().logger.write_warning(_("Error: BACnet server '%s.x: %s' contains objects with duplicate object identifiers.\n")%(locstr, self.CTNName())) |
579 self.GetCTRoot().logger.write_warning( |
537 raise Exception, False |
580 _("Error: BACnet server '%s.x: %s' contains objects with duplicate object identifiers.\n") % (locstr, self.CTNName())) |
538 # TODO: return an error code instead of raising an exception (currently unsupported by Beremiz) |
581 raise Exception(False) |
539 |
582 # TODO: return an error code instead of raising an exception |
540 #------------------------------------------------------------------------------- |
583 # (currently unsupported by Beremiz) |
|
584 |
|
585 # ------------------------------------------------------------------------------- |
541 # Create and populate the loc_dict dictionary with all parameters needed to configure |
586 # Create and populate the loc_dict dictionary with all parameters needed to configure |
542 # the generated source code (.c and .h files) |
587 # the generated source code (.c and .h files) |
543 #------------------------------------------------------------------------------- |
588 # ---------------------------------------------------------------------- |
544 |
589 |
545 # 1) Create the dictionary (loc_dict = {}) |
590 # 1) Create the dictionary (loc_dict = {}) |
546 loc_dict = {} |
591 loc_dict = {} |
547 loc_dict["locstr"] = locstr |
592 loc_dict["locstr"] = locstr |
548 |
593 |
549 #The BACnetServerNode attribute is added dynamically by ConfigTreeNode._AddParamsMembers() |
594 # The BACnetServerNode attribute is added dynamically by ConfigTreeNode._AddParamsMembers() |
550 # It will be an XML parser object created by GenerateParserFromXSDstring(self.XSD).CreateRoot() |
595 # It will be an XML parser object created by |
551 loc_dict["network_interface"] = self.BACnetServerNode.getNetwork_Interface() |
596 # GenerateParserFromXSDstring(self.XSD).CreateRoot() |
552 loc_dict["port_number"] = self.BACnetServerNode.getUDP_Port_Number() |
597 loc_dict["network_interface"] = self.BACnetServerNode.getNetwork_Interface() |
553 loc_dict["BACnet_Device_ID"] = self.BACnetServerNode.getBACnet_Device_ID() |
598 loc_dict["port_number"] = self.BACnetServerNode.getUDP_Port_Number() |
554 loc_dict["BACnet_Device_Name"] = self.BACnetServerNode.getBACnet_Device_Name() |
599 loc_dict["BACnet_Device_ID"] = self.BACnetServerNode.getBACnet_Device_ID() |
|
600 loc_dict["BACnet_Device_Name"] = self.BACnetServerNode.getBACnet_Device_Name() |
555 loc_dict["BACnet_Comm_Control_Password"] = self.BACnetServerNode.getBACnet_Communication_Control_Password() |
601 loc_dict["BACnet_Comm_Control_Password"] = self.BACnetServerNode.getBACnet_Communication_Control_Password() |
556 loc_dict["BACnet_Device_Location"] = self.BACnetServerNode.getBACnet_Device_Location() |
602 loc_dict["BACnet_Device_Location"] = self.BACnetServerNode.getBACnet_Device_Location() |
557 loc_dict["BACnet_Device_Description"] = self.BACnetServerNode.getBACnet_Device_Description() |
603 loc_dict["BACnet_Device_Description"] = self.BACnetServerNode.getBACnet_Device_Description() |
558 loc_dict["BACnet_Device_AppSoft_Version"]= self.BACnetServerNode.getBACnet_Device_Application_Software_Version() |
604 loc_dict["BACnet_Device_AppSoft_Version"] = self.BACnetServerNode.getBACnet_Device_Application_Software_Version() |
559 loc_dict["BACnet_Vendor_ID"] = BACNET_VENDOR_ID |
605 loc_dict["BACnet_Vendor_ID"] = BACNET_VENDOR_ID |
560 loc_dict["BACnet_Vendor_Name"] = BACNET_VENDOR_NAME |
606 loc_dict["BACnet_Vendor_Name"] = BACNET_VENDOR_NAME |
561 loc_dict["BACnet_Model_Name"] = BACNET_DEVICE_MODEL_NAME |
607 loc_dict["BACnet_Model_Name"] = BACNET_DEVICE_MODEL_NAME |
562 |
608 |
563 # 2) Add the data specific to each BACnet object type |
609 # 2) Add the data specific to each BACnet object type |
564 # For each BACnet object type, start off by creating some intermediate helpful lists |
610 # For each BACnet object type, start off by creating some intermediate helpful lists |
565 # a) parameters_list containing the strings that will |
611 # a) parameters_list containing the strings that will |
566 # be included in the C source code, and which will initialize the struct with the |
612 # be included in the C source code, and which will initialize the struct with the |
567 # object (Analog Value, Binary Value, or Multi State Value) parameters |
613 # object (Analog Value, Binary Value, or Multi State Value) parameters |
568 # b) locatedvar_list containing the strings that will |
614 # b) locatedvar_list containing the strings that will |
569 # declare the memory to store the located variables, as well as the |
615 # declare the memory to store the located variables, as well as the |
570 # pointers (required by matiec) that point to that memory. |
616 # pointers (required by matiec) that point to that memory. |
571 |
617 |
572 # format for delaring IEC 61131-3 variable (and pointer) onto which BACnet object is mapped |
618 # format for delaring IEC 61131-3 variable (and pointer) onto which |
|
619 # BACnet object is mapped |
573 locvar_format = '%(Ctype)s ___%(loc)s_%(Object Identifier)s; ' + \ |
620 locvar_format = '%(Ctype)s ___%(loc)s_%(Object Identifier)s; ' + \ |
574 '%(Ctype)s *__%(loc)s_%(Object Identifier)s = &___%(loc)s_%(Object Identifier)s;' |
621 '%(Ctype)s *__%(loc)s_%(Object Identifier)s = &___%(loc)s_%(Object Identifier)s;' |
575 |
622 |
576 # format for initializing a ANALOG_VALUE_DESCR struct in C code |
623 # format for initializing a ANALOG_VALUE_DESCR struct in C code |
577 # also valid for ANALOG_INPUT and ANALOG_OUTPUT |
624 # also valid for ANALOG_INPUT and ANALOG_OUTPUT |
578 AX_params_format = '{&___%(loc)s_%(Object Identifier)s, ' + \ |
625 AX_params_format = '{&___%(loc)s_%(Object Identifier)s, ' + \ |
579 '%(Object Identifier)s, "%(Object Name)s", "%(Description)s", %(Unit ID)d}' |
626 '%(Object Identifier)s, "%(Object Name)s", "%(Description)s", %(Unit ID)d}' |
580 # format for initializing a BINARY_VALUE_DESCR struct in C code |
627 # format for initializing a BINARY_VALUE_DESCR struct in C code |
581 # also valid for BINARY_INPUT and BINARY_OUTPUT |
628 # also valid for BINARY_INPUT and BINARY_OUTPUT |
582 BX_params_format = '{&___%(loc)s_%(Object Identifier)s, ' + \ |
629 BX_params_format = '{&___%(loc)s_%(Object Identifier)s, ' + \ |
583 '%(Object Identifier)s, "%(Object Name)s", "%(Description)s"}' |
630 '%(Object Identifier)s, "%(Object Name)s", "%(Description)s"}' |
584 |
631 |
585 # format for initializing a MULTISTATE_VALUE_DESCR struct in C code |
632 # format for initializing a MULTISTATE_VALUE_DESCR struct in C code |
586 # also valid for MULTISTATE_INPUT and MULTISTATE_OUTPUT |
633 # also valid for MULTISTATE_INPUT and MULTISTATE_OUTPUT |
587 MSX_params_format = '{&___%(loc)s_%(Object Identifier)s, ' + \ |
634 MSX_params_format = '{&___%(loc)s_%(Object Identifier)s, ' + \ |
588 '%(Object Identifier)s, "%(Object Name)s", "%(Description)s", %(Number of States)s}' |
635 '%(Object Identifier)s, "%(Object Name)s", "%(Description)s", %(Number of States)s}' |
589 |
636 |
590 AV_locstr = 'MD' + locstr+'_2' # see the comment in GetVariableLocationTree() to grok the '_2' |
637 # see the comment in GetVariableLocationTree() |
591 AO_locstr = 'QD' + locstr+'_1' # see the comment in GetVariableLocationTree() to grok the '_1' |
638 AV_locstr = 'MD' + locstr + '_2' |
592 AI_locstr = 'ID' + locstr+'_0' # see the comment in GetVariableLocationTree() to grok the '_0' |
639 AO_locstr = 'QD' + locstr + '_1' |
593 BV_locstr = 'MX' + locstr+'_5' # see the comment in GetVariableLocationTree() to grok the '_5' |
640 AI_locstr = 'ID' + locstr + '_0' |
594 BO_locstr = 'QX' + locstr+'_4' # see the comment in GetVariableLocationTree() to grok the '_4' |
641 BV_locstr = 'MX' + locstr + '_5' |
595 BI_locstr = 'IX' + locstr+'_3' # see the comment in GetVariableLocationTree() to grok the '_3' |
642 BO_locstr = 'QX' + locstr + '_4' |
596 MSV_locstr = 'MB' + locstr+'_19' # see the comment in GetVariableLocationTree() to grok the '_19' |
643 BI_locstr = 'IX' + locstr + '_3' |
597 MSO_locstr = 'QB' + locstr+'_14' # see the comment in GetVariableLocationTree() to grok the '_14' |
644 MSV_locstr = 'MB' + locstr + '_19' |
598 MSI_locstr = 'IB' + locstr+'_13' # see the comment in GetVariableLocationTree() to grok the '_13' |
645 MSO_locstr = 'QB' + locstr + '_14' |
599 |
646 MSI_locstr = 'IB' + locstr + '_13' |
600 |
647 |
601 for ObjType, ObjLocStr, params_format in [ |
648 for ObjType, ObjLocStr, params_format in [ |
602 ("AV" , AV_locstr, AX_params_format ), |
649 ("AV", AV_locstr, AX_params_format), |
603 ("AO" , AO_locstr, AX_params_format ), |
650 ("AO", AO_locstr, AX_params_format), |
604 ("AI" , AI_locstr, AX_params_format ), |
651 ("AI", AI_locstr, AX_params_format), |
605 ("BV" , BV_locstr, BX_params_format ), |
652 ("BV", BV_locstr, BX_params_format), |
606 ("BO" , BO_locstr, BX_params_format ), |
653 ("BO", BO_locstr, BX_params_format), |
607 ("BI" , BI_locstr, BX_params_format ), |
654 ("BI", BI_locstr, BX_params_format), |
608 ("MSV" , MSV_locstr, MSX_params_format ), |
655 ("MSV", MSV_locstr, MSX_params_format), |
609 ("MSO" , MSO_locstr, MSX_params_format ), |
656 ("MSO", MSO_locstr, MSX_params_format), |
610 ("MSI" , MSI_locstr, MSX_params_format ) |
657 ("MSI", MSI_locstr, MSX_params_format)]: |
611 ]: |
|
612 parameters_list = [] |
658 parameters_list = [] |
613 locatedvar_list = [] |
659 locatedvar_list = [] |
614 self.ObjTables[ObjType + "_Obj"].UpdateAllVirtualProperties() |
660 self.ObjTables[ObjType + "_Obj"].UpdateAllVirtualProperties() |
615 for ObjProp in self.ObjTablesData[ObjType + "_Obj"]: |
661 for ObjProp in self.ObjTablesData[ObjType + "_Obj"]: |
616 ObjProp["loc" ] = ObjLocStr |
662 ObjProp["loc"] = ObjLocStr |
617 parameters_list.append(params_format % ObjProp) |
663 parameters_list.append(params_format % ObjProp) |
618 locatedvar_list.append(locvar_format % ObjProp) |
664 locatedvar_list.append(locvar_format % ObjProp) |
619 loc_dict[ ObjType + "_count"] = len( parameters_list ) |
665 loc_dict[ObjType + "_count"] = len(parameters_list) |
620 loc_dict[ ObjType + "_param"] = ",\n".join( parameters_list ) |
666 loc_dict[ObjType + "_param"] = ",\n".join(parameters_list) |
621 loc_dict[ ObjType + "_lvars"] = "\n".join( locatedvar_list ) |
667 loc_dict[ObjType + "_lvars"] = "\n".join(locatedvar_list) |
622 |
668 |
623 #---------------------------------------------------------------------- |
669 # ---------------------------------------------------------------------- |
624 # Create the C source files that implement the BACnet server |
670 # Create the C source files that implement the BACnet server |
625 #---------------------------------------------------------------------- |
671 # ---------------------------------------------------------------------- |
626 |
672 |
627 # Names of the .c files that will be generated, based on a template file with same name |
673 # Names of the .c files that will be generated, based on a template file with same name |
628 # (names without '.c' --> this will be added later) |
674 # (names without '.c' --> this will be added later) |
629 # main server.c file is handled separately |
675 # main server.c file is handled separately |
630 Generated_BACnet_c_mainfile = "server" |
676 Generated_BACnet_c_mainfile = "server" |
631 Generated_BACnet_c_files = ["ai", "ao", "av", "bi", "bo", "bv", "msi", "mso", "msv", "device"] |
677 Generated_BACnet_c_files = [ |
|
678 "ai", "ao", "av", "bi", "bo", "bv", "msi", "mso", "msv", "device"] |
632 |
679 |
633 # Names of the .h files that will be generated, based on a template file with same name |
680 # Names of the .h files that will be generated, based on a template file with same name |
634 # (names without '.h' --> this will be added later) |
681 # (names without '.h' --> this will be added later) |
635 Generated_BACnet_h_files = ["server", "device", "config_bacnet_for_beremiz", |
682 Generated_BACnet_h_files = [ |
636 "ai", "ao", "av", "bi", "bo", "bv", "msi", "mso", "msv" |
683 "server", "device", "config_bacnet_for_beremiz", |
637 ] |
684 "ai", "ao", "av", "bi", "bo", "bv", "msi", "mso", "msv" |
|
685 ] |
638 |
686 |
639 # Generate the files with the source code |
687 # Generate the files with the source code |
640 postfix = "_".join(map(str, current_location)) |
688 postfix = "_".join(map(str, current_location)) |
641 template_file_dir = os.path.join(os.path.split(__file__)[0],"runtime") |
689 template_file_dir = os.path.join( |
642 |
690 os.path.split(__file__)[0], "runtime") |
643 def generate_file(file_name, extension): |
691 |
644 generate_file_name = os.path.join(buildpath, "%s_%s.%s"%(file_name,postfix,extension)) |
692 def generate_file(file_name, extension): |
645 template_file_name = os.path.join(template_file_dir,"%s.%s"%(file_name,extension)) |
693 generate_file_name = os.path.join( |
|
694 buildpath, "%s_%s.%s" % (file_name, postfix, extension)) |
|
695 template_file_name = os.path.join( |
|
696 template_file_dir, "%s.%s" % (file_name, extension)) |
646 generate_file_content = open(template_file_name).read() % loc_dict |
697 generate_file_content = open(template_file_name).read() % loc_dict |
647 generate_file_handle = open(generate_file_name,'w') |
698 generate_file_handle = open(generate_file_name, 'w') |
648 generate_file_handle .write(generate_file_content) |
699 generate_file_handle.write(generate_file_content) |
649 generate_file_handle .close() |
700 generate_file_handle.close() |
650 |
701 |
651 for file_name in Generated_BACnet_c_files: |
702 for file_name in Generated_BACnet_c_files: |
652 generate_file(file_name, "c") |
703 generate_file(file_name, "c") |
653 for file_name in Generated_BACnet_h_files: |
704 for file_name in Generated_BACnet_h_files: |
654 generate_file(file_name, "h") |
705 generate_file(file_name, "h") |
655 generate_file(Generated_BACnet_c_mainfile, "c") |
706 generate_file(Generated_BACnet_c_mainfile, "c") |
656 Generated_BACnet_c_mainfile_name = \ |
707 Generated_BACnet_c_mainfile_name = \ |
657 os.path.join(buildpath, "%s_%s.%s"%(Generated_BACnet_c_mainfile,postfix,"c")) |
708 os.path.join(buildpath, "%s_%s.%s" % |
658 |
709 (Generated_BACnet_c_mainfile, postfix, "c")) |
659 #---------------------------------------------------------------------- |
710 |
|
711 # ---------------------------------------------------------------------- |
660 # Finally, define the compilation and linking commands and flags |
712 # Finally, define the compilation and linking commands and flags |
661 #---------------------------------------------------------------------- |
713 # ---------------------------------------------------------------------- |
662 |
714 |
663 LDFLAGS = [] |
715 LDFLAGS = [] |
664 # when using dynamically linked library... |
716 # when using dynamically linked library... |
665 #LDFLAGS.append(' -lbacnet') |
717 # LDFLAGS.append(' -lbacnet') |
666 #LDFLAGS.append(' -L"'+BacnetLibraryPath+'"') |
718 # LDFLAGS.append(' -L"'+BacnetLibraryPath+'"') |
667 #LDFLAGS.append(' "-Wl,-rpath,' + BacnetLibraryPath + '"') |
719 # LDFLAGS.append(' "-Wl,-rpath,' + BacnetLibraryPath + '"') |
668 # when using static library: |
720 # when using static library: |
669 LDFLAGS.append(' "'+os.path.join(BacnetLibraryPath, "libbacnet.a")+'"') |
721 LDFLAGS.append( |
670 |
722 ' "' + os.path.join(BacnetLibraryPath, "libbacnet.a") + '"') |
671 CFLAGS = ' -I"'+BacnetIncludePath+'"' |
723 |
672 CFLAGS += ' -I"'+BacnetIncludePortPath+'"' |
724 CFLAGS = ' -I"' + BacnetIncludePath + '"' |
673 |
725 CFLAGS += ' -I"' + BacnetIncludePortPath + '"' |
|
726 |
674 return [(Generated_BACnet_c_mainfile_name, CFLAGS)], LDFLAGS, True |
727 return [(Generated_BACnet_c_mainfile_name, CFLAGS)], LDFLAGS, True |
675 |
|
676 |
|
677 |
|
678 ################################################### |
|
679 ################################################### |
|
680 # # |
|
681 # R O O T C L A S S # |
|
682 # # |
|
683 ################################################### |
|
684 ################################################### |
|
685 #class RootClass: |
|
686 #XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
|
687 #<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
|
688 #<xsd:element name="BACnetRoot"> |
|
689 #</xsd:element> |
|
690 #</xsd:schema> |
|
691 #""" |
|
692 #CTNChildrenTypes = [("BacnetSlave", _BacnetSlavePlug, "Bacnet Slave") |
|
693 ##,("XXX",_XXXXPlug, "XXX") |
|
694 #] |
|
695 |
|
696 #def CTNGenerate_C(self, buildpath, locations): |
|
697 #return [], "", True |
|