bacnet/bacnet.py
changeset 2250 86f61c4dfe76
parent 2020 6dddf3070806
child 2309 d8fb90a2e11f
equal deleted inserted replaced
2249:602fdd08dfab 2250:86f61c4dfe76
    17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    18 # GNU General Public License for more details.
    18 # GNU General Public License for more details.
    19 #
    19 #
    20 # You should have received a copy of the GNU General Public License
    20 # You should have received a copy of the GNU General Public License
    21 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
    21 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
    22 #    
    22 #
    23 # This code is made available on the understanding that it will not be
    23 # This code is made available on the understanding that it will not be
    24 # used in safety-critical situations without a full and competent review.
    24 # used in safety-critical situations without a full and competent review.
    25 
    25 
    26 
    26 from __future__ import absolute_import
    27 
    27 
    28 import os, sys
    28 import os
    29 from collections import Counter
    29 from collections import Counter
    30 from datetime    import datetime
    30 from datetime import datetime
    31 
    31 import pickle
    32 base_folder           = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]
    32 
    33 base_folder           = os.path.join(base_folder, "..")
    33 import wx
    34 BacnetPath            = os.path.join(base_folder, "BACnet")
    34 
    35 BacnetLibraryPath     = os.path.join(BacnetPath, "lib")
    35 from bacnet.BacnetSlaveEditor import *
    36 BacnetIncludePath     = os.path.join(BacnetPath, "include")
    36 from bacnet.BacnetSlaveEditor import ObjectProperties
       
    37 from PLCControler import LOCATION_CONFNODE, LOCATION_VAR_MEMORY
       
    38 
       
    39 base_folder = os.path.split(
       
    40     os.path.dirname(os.path.realpath(__file__)))[0]
       
    41 base_folder = os.path.join(base_folder, "..")
       
    42 BacnetPath = os.path.join(base_folder, "BACnet")
       
    43 BacnetLibraryPath = os.path.join(BacnetPath, "lib")
       
    44 BacnetIncludePath = os.path.join(BacnetPath, "include")
    37 BacnetIncludePortPath = os.path.join(BacnetPath, "ports")
    45 BacnetIncludePortPath = os.path.join(BacnetPath, "ports")
    38 BacnetIncludePortPath = os.path.join(BacnetIncludePortPath, "linux")
    46 BacnetIncludePortPath = os.path.join(BacnetIncludePortPath, "linux")
    39 
    47 
    40 import wx
       
    41 import pickle
       
    42 
       
    43 from BacnetSlaveEditor import *
       
    44 from BacnetSlaveEditor import ObjectProperties
       
    45 from ConfigTreeNode    import ConfigTreeNode
       
    46 from PLCControler      import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
       
    47 
       
    48 # Parameters to be monkey patched in beremiz customizations
    48 # Parameters to be monkey patched in beremiz customizations
    49 BACNET_VENDOR_ID = 9999 
    49 BACNET_VENDOR_ID = 9999
    50 BACNET_VENDOR_NAME = "Beremiz.org"
    50 BACNET_VENDOR_NAME = "Beremiz.org"
    51 BACNET_DEVICE_MODEL_NAME = "Beremiz PLC"
    51 BACNET_DEVICE_MODEL_NAME = "Beremiz PLC"
    52 
    52 
    53 ###################################################
    53 #
    54 ###################################################
    54 #
    55 #                                                 #
    55 #
    56 #           S L A V E    D E V I C E              # 
    56 # S L A V E    D E V I C E              #
    57 #                                                 #
    57 #
    58 ###################################################
    58 #
    59 ###################################################
    59 #
    60 
    60 
    61 # NOTE: Objects of class _BacnetSlavePlug are never instantiated directly.
    61 # NOTE: Objects of class _BacnetSlavePlug are never instantiated directly.
    62 #       The objects are instead instantiated from class FinalCTNClass
    62 #       The objects are instead instantiated from class FinalCTNClass
    63 #       FinalCTNClass inherits from: - ConfigTreeNode
    63 #       FinalCTNClass inherits from: - ConfigTreeNode
    64 #                                    - The tree node plug (in our case _BacnetSlavePlug)
    64 #                                    - The tree node plug (in our case _BacnetSlavePlug)
    65 #class _BacnetSlavePlug:
    65 # class _BacnetSlavePlug:
    66 class RootClass:
    66 
       
    67 
       
    68 class RootClass(object):
    67     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
    69     XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
    68     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    70     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    69       <xsd:element name="BACnetServerNode">
    71       <xsd:element name="BACnetServerNode">
    70         <xsd:complexType>
    72         <xsd:complexType>
    71           <xsd:attribute name="Network_Interface"      type="xsd:string"  use="optional" default="eth0"/>
    73           <xsd:attribute name="Network_Interface"      type="xsd:string"  use="optional" default="eth0"/>
    75                     <xsd:minInclusive value="0"/>
    77                     <xsd:minInclusive value="0"/>
    76                     <xsd:maxInclusive value="65535"/>
    78                     <xsd:maxInclusive value="65535"/>
    77                 </xsd:restriction>
    79                 </xsd:restriction>
    78             </xsd:simpleType>
    80             </xsd:simpleType>
    79           </xsd:attribute>
    81           </xsd:attribute>
    80           <xsd:attribute name="BACnet_Communication_Control_Password"     
    82           <xsd:attribute name="BACnet_Communication_Control_Password"
    81                                                        type="xsd:string"  use="optional" default="Malba Tahan"/>
    83                                                        type="xsd:string"  use="optional" default="Malba Tahan"/>
    82           <xsd:attribute name="BACnet_Device_ID"                          use="optional" default="0">
    84           <xsd:attribute name="BACnet_Device_ID"                          use="optional" default="0">
    83             <xsd:simpleType>
    85             <xsd:simpleType>
    84                 <xsd:restriction base="xsd:integer">
    86                 <xsd:restriction base="xsd:integer">
    85                     <xsd:minInclusive value="0"/>
    87                     <xsd:minInclusive value="0"/>
    97     """
    99     """
    98     # NOTE: BACnet device (object) IDs are 22 bits long (not counting the 10 bits for the type ID)
   100     # NOTE: BACnet device (object) IDs are 22 bits long (not counting the 10 bits for the type ID)
    99     #       so the Device instance ID is limited from 0 to 22^2-1 = 4194303
   101     #       so the Device instance ID is limited from 0 to 22^2-1 = 4194303
   100     #       However, 4194303 is reserved for special use (similar to NULL pointer), so last
   102     #       However, 4194303 is reserved for special use (similar to NULL pointer), so last
   101     #       valid ID becomes 4194302
   103     #       valid ID becomes 4194302
   102     
   104 
   103     
       
   104     # The class/object that will render the graphical interface to edit the
   105     # The class/object that will render the graphical interface to edit the
   105     #    BacnetSlavePlug's configuration parameters. The object of class BacnetSlaveEditorPlug
   106     #    BacnetSlavePlug's configuration parameters. The object of class BacnetSlaveEditorPlug
   106     #    will be instantiated by the ConfigTreeNode class.
   107     #    will be instantiated by the ConfigTreeNode class.
   107     #    This BacnetSlaveEditorPlug object can be accessed from _BacnetSlavePlug as
   108     #    This BacnetSlaveEditorPlug object can be accessed from _BacnetSlavePlug as
   108     #    'self._View'
   109     #    'self._View'
   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