bacnet/web_settings.py
changeset 2686 703ebf57508a
parent 2676 b276d05bdb09
child 2703 32ffdb32b14e
equal deleted inserted replaced
2685:f71c22b2ca25 2686:703ebf57508a
    32 
    32 
    33 
    33 
    34 # Will contain references to the C functions 
    34 # Will contain references to the C functions 
    35 # (implemented in beremiz/bacnet/runtime/server.c)
    35 # (implemented in beremiz/bacnet/runtime/server.c)
    36 # used to get/set the BACnet specific configuration paramters
    36 # used to get/set the BACnet specific configuration paramters
    37 GetParamFuncs = {}
    37 BacnetGetParamFuncs = {}
    38 SetParamFuncs = {}
    38 BacnetSetParamFuncs = {}
    39 
    39 
    40 
    40 
    41 # Upon PLC load, this Dictionary is initialised with the BACnet configuration
    41 # Upon PLC load, this Dictionary is initialised with the BACnet configuration
    42 # hardcoded in the C file
    42 # hardcoded in the C file
    43 # (i.e. the configuration inserted in Beremiz IDE when project was compiled)
    43 # (i.e. the configuration inserted in Beremiz IDE when project was compiled)
    44 _DefaultConfiguration = None
    44 _BacnetDefaultConfiguration = None
    45 
    45 
    46 
    46 
    47 # Dictionary that contains the BACnet configuration currently being shown
    47 # Dictionary that contains the BACnet configuration currently being shown
    48 # on the web interface
    48 # on the web interface
    49 # This configuration will almost always be identical to the current
    49 # This configuration will almost always be identical to the current
    50 # configuration in the PLC (i.e., the current state stored in the 
    50 # configuration in the PLC (i.e., the current state stored in the 
    51 # C variables in the .so file).
    51 # C variables in the .so file).
    52 # The configuration viewed on the web will only be different to the current 
    52 # The configuration viewed on the web will only be different to the current 
    53 # configuration when the user edits the configuration, and when
    53 # configuration when the user edits the configuration, and when
    54 # the user asks to save the edited configuration but it contains an error.
    54 # the user asks to save the edited configuration but it contains an error.
    55 _WebviewConfiguration = None
    55 _BacnetWebviewConfiguration = None
    56 
    56 
    57 
    57 
    58 # Dictionary that stores the BACnet configuration currently stored in a file
    58 # Dictionary that stores the BACnet configuration currently stored in a file
    59 # Currently only used to decide whether or not to show the "Delete" button on the
    59 # Currently only used to decide whether or not to show the "Delete" button on the
    60 # web interface (only shown if _SavedConfiguration is not None)
    60 # web interface (only shown if _BacnetSavedConfiguration is not None)
    61 _SavedConfiguration = None
    61 _BacnetSavedConfiguration = None
    62 
    62 
    63 
    63 
    64 # File to which the new BACnet configuration gets stored on the PLC
    64 # File to which the new BACnet configuration gets stored on the PLC
    65 # Note that the stored configuration is likely different to the
    65 # Note that the stored configuration is likely different to the
    66 # configuration hardcoded in C generated code (.so file), so
    66 # configuration hardcoded in C generated code (.so file), so
    97 
    97 
    98 
    98 
    99 
    99 
   100 
   100 
   101 
   101 
   102 def _CheckPortnumber(port_number):
   102 def _CheckBacnetPortnumber(port_number):
   103     """ check validity of the port number """
   103     """ check validity of the port number """
   104     try:
   104     try:
   105         portnum = int(port_number)
   105         portnum = int(port_number)
   106         if (portnum < 0) or (portnum > 65535):
   106         if (portnum < 0) or (portnum > 65535):
   107            raise Exception
   107            raise Exception
   110         
   110         
   111     return True    
   111     return True    
   112     
   112     
   113 
   113 
   114 
   114 
   115 def _CheckDeviceID(device_id):
   115 def _CheckBacnetDeviceID(device_id):
   116     """ 
   116     """ 
   117     # check validity of the Device ID 
   117     # check validity of the Device ID 
   118     # NOTE: BACnet device (object) IDs are 22 bits long (not counting the 10 bits for the type ID)
   118     # NOTE: BACnet device (object) IDs are 22 bits long (not counting the 10 bits for the type ID)
   119     #       so the Device instance ID is limited from 0 to 22^2-1 = 4194303
   119     #       so the Device instance ID is limited from 0 to 22^2-1 = 4194303
   120     #       However, 4194303 is reserved for special use (similar to NULL pointer), so last
   120     #       However, 4194303 is reserved for special use (similar to NULL pointer), so last
   131 
   131 
   132 
   132 
   133 
   133 
   134 
   134 
   135 
   135 
   136 def _CheckConfiguration(BACnetConfig):
   136 def _CheckBacnetConfiguration(BACnetConfig):
   137     res = True    
   137     res = True    
   138     res = res and _CheckPortnumber(BACnetConfig["port_number"])
   138     res = res and _CheckBacnetPortnumber(BACnetConfig["port_number"])
   139     res = res and _CheckDeviceID  (BACnetConfig["device_id"])
   139     res = res and _CheckBacnetDeviceID  (BACnetConfig["device_id"])
   140     return res
   140     return res
   141 
   141 
   142 
   142 
   143 
   143 
   144 def _CheckWebConfiguration(BACnetConfig):
   144 def _CheckBacnetWebConfiguration(BACnetConfig):
   145     res = True
   145     res = True
   146     
   146     
   147     # check the port number
   147     # check the port number
   148     if not _CheckPortnumber(BACnetConfig["port_number"]):
   148     if not _CheckBacnetPortnumber(BACnetConfig["port_number"]):
   149         raise annotate.ValidateError(
   149         raise annotate.ValidateError(
   150             {"port_number": "Invalid port number: " + str(BACnetConfig["port_number"])},
   150             {"port_number": "Invalid port number: " + str(BACnetConfig["port_number"])},
   151             _("BACnet configuration error:"))
   151             _("BACnet configuration error:"))
   152         res = False
   152         res = False
   153     
   153     
   154     if not _CheckDeviceID(BACnetConfig["device_id"]):
   154     if not _CheckBacnetDeviceID(BACnetConfig["device_id"]):
   155         raise annotate.ValidateError(
   155         raise annotate.ValidateError(
   156             {"device_id": "Invalid device ID: " + str(BACnetConfig["device_id"])},
   156             {"device_id": "Invalid device ID: " + str(BACnetConfig["device_id"])},
   157             _("BACnet configuration error:"))
   157             _("BACnet configuration error:"))
   158         res = False
   158         res = False
   159         
   159         
   162 
   162 
   163 
   163 
   164 
   164 
   165 
   165 
   166 
   166 
   167 def _SetSavedConfiguration(BACnetConfig):
   167 def _SetBacnetSavedConfiguration(BACnetConfig):
   168     """ Stores in a file a dictionary containing the BACnet parameter configuration """
   168     """ Stores in a file a dictionary containing the BACnet parameter configuration """
   169     global _SavedConfiguration
   169     global _BacnetSavedConfiguration
   170 
   170 
   171     if BACnetConfig == _DefaultConfiguration :
   171     if BACnetConfig == _BacnetDefaultConfiguration :
   172         _DelSavedConfiguration()
   172         _DelBacnetSavedConfiguration()
   173         _SavedConfiguration = None
   173         _BacnetSavedConfiguration = None
   174     else :
   174     else :
   175         with open(os.path.realpath(_BACnetConfFilename), 'w') as f:
   175         with open(os.path.realpath(_BACnetConfFilename), 'w') as f:
   176             json.dump(BACnetConfig, f, sort_keys=True, indent=4)
   176             json.dump(BACnetConfig, f, sort_keys=True, indent=4)
   177         _SavedConfiguration = BACnetConfig
   177         _BacnetSavedConfiguration = BACnetConfig
   178 
   178 
   179 
   179 
   180 def _DelSavedConfiguration():
   180 def _DelBacnetSavedConfiguration():
   181     """ Deletes the file cotaining the persistent BACnet configuration """
   181     """ Deletes the file cotaining the persistent BACnet configuration """
   182     if os.path.exists(_BACnetConfFilename):
   182     if os.path.exists(_BACnetConfFilename):
   183         os.remove(_BACnetConfFilename)
   183         os.remove(_BACnetConfFilename)
   184 
   184 
   185 
   185 
   186 def _GetSavedConfiguration():
   186 def _GetBacnetSavedConfiguration():
   187     """
   187     """
   188     # Returns a dictionary containing the BACnet parameter configuration
   188     # Returns a dictionary containing the BACnet parameter configuration
   189     # that was last saved to file. If no file exists, then return None
   189     # that was last saved to file. If no file exists, then return None
   190     """
   190     """
   191     try:
   191     try:
   192         #if os.path.isfile(_BACnetConfFilename):
   192         #if os.path.isfile(_BACnetConfFilename):
   193         saved_config = json.load(open(_BACnetConfFilename))
   193         saved_config = json.load(open(_BACnetConfFilename))
   194     except Exception:    
   194     except Exception:    
   195         return None
   195         return None
   196 
   196 
   197     if _CheckConfiguration(saved_config):
   197     if _CheckBacnetConfiguration(saved_config):
   198         return saved_config
   198         return saved_config
   199     else:
   199     else:
   200         return None
   200         return None
   201 
   201 
   202 
   202 
   203 def _GetPLCConfiguration():
   203 def _GetBacnetPLCConfiguration():
   204     """
   204     """
   205     # Returns a dictionary containing the current BACnet parameter configuration
   205     # Returns a dictionary containing the current BACnet parameter configuration
   206     # stored in the C variables in the loaded PLC (.so file)
   206     # stored in the C variables in the loaded PLC (.so file)
   207     """
   207     """
   208     current_config = {}
   208     current_config = {}
   209     for par_name, x1, x2, x3 in BACnet_parameters:
   209     for par_name, x1, x2, x3 in BACnet_parameters:
   210         value = GetParamFuncs[par_name]()
   210         value = BacnetGetParamFuncs[par_name]()
   211         if value is not None:
   211         if value is not None:
   212             current_config[par_name] = value
   212             current_config[par_name] = value
   213     
   213     
   214     return current_config
   214     return current_config
   215 
   215 
   216 
   216 
   217 def _SetPLCConfiguration(BACnetConfig):
   217 def _SetBacnetPLCConfiguration(BACnetConfig):
   218     """
   218     """
   219     # Stores the BACnet parameter configuration into the
   219     # Stores the BACnet parameter configuration into the
   220     # the C variables in the loaded PLC (.so file)
   220     # the C variables in the loaded PLC (.so file)
   221     """
   221     """
   222     for par_name in BACnetConfig:
   222     for par_name in BACnetConfig:
   223         value = BACnetConfig[par_name]
   223         value = BACnetConfig[par_name]
   224         #PLCObject.LogMessage("BACnet web server extension::_SetPLCConfiguration()  Setting "
   224         #PLCObject.LogMessage("BACnet web server extension::_SetBacnetPLCConfiguration()  Setting "
   225         #                       + par_name + " to " + str(value) )
   225         #                       + par_name + " to " + str(value) )
   226         if value is not None:
   226         if value is not None:
   227             SetParamFuncs[par_name](value)
   227             BacnetSetParamFuncs[par_name](value)
   228     # update the configuration shown on the web interface
   228     # update the configuration shown on the web interface
   229     global _WebviewConfiguration 
   229     global _BacnetWebviewConfiguration 
   230     _WebviewConfiguration = _GetPLCConfiguration()
   230     _BacnetWebviewConfiguration = _GetBacnetPLCConfiguration()
   231 
   231 
   232 
   232 
   233 
   233 
   234 def _GetWebviewConfigurationValue(ctx, argument):
   234 def _GetBacnetWebviewConfigurationValue(ctx, argument):
   235     """
   235     """
   236     # Callback function, called by the web interface (NevowServer.py)
   236     # Callback function, called by the web interface (NevowServer.py)
   237     # to fill in the default value of each parameter
   237     # to fill in the default value of each parameter
   238     """
   238     """
   239     try:
   239     try:
   240         return _WebviewConfiguration[argument.name]
   240         return _BacnetWebviewConfiguration[argument.name]
   241     except Exception:
   241     except Exception:
   242         return ""
   242         return ""
   243 
   243 
   244 
   244 
   245 # The configuration of the web form used to see/edit the BACnet parameters
   245 # The configuration of the web form used to see/edit the BACnet parameters
   246 webFormInterface = [(name, web_dtype (label=web_label, default=_GetWebviewConfigurationValue)) 
   246 webFormInterface = [(name, web_dtype (label=web_label, default=_GetBacnetWebviewConfigurationValue)) 
   247                     for name, web_label, c_dtype, web_dtype in BACnet_parameters]
   247                     for name, web_label, c_dtype, web_dtype in BACnet_parameters]
   248 
   248 
   249 
   249 
   250 def OnButtonSave(**kwargs):
   250 def OnBacnetButtonSave(**kwargs):
   251     """
   251     """
   252     # Function called when user clicks 'Save' button in web interface
   252     # Function called when user clicks 'Save' button in web interface
   253     # The function will configure the BACnet plugin in the PLC with the values
   253     # The function will configure the BACnet plugin in the PLC with the values
   254     # specified in the web interface. However, values must be validated first!
   254     # specified in the web interface. However, values must be validated first!
   255     """
   255     """
   256 
   256 
   257     #PLCObject.LogMessage("BACnet web server extension::OnButtonSave()  Called")
   257     #PLCObject.LogMessage("BACnet web server extension::OnBacnetButtonSave()  Called")
   258     
   258     
   259     newConfig = {}
   259     newConfig = {}
   260     for par_name, x1, x2, x3 in BACnet_parameters:
   260     for par_name, x1, x2, x3 in BACnet_parameters:
   261         value = kwargs.get(par_name, None)
   261         value = kwargs.get(par_name, None)
   262         if value is not None:
   262         if value is not None:
   263             newConfig[par_name] = value
   263             newConfig[par_name] = value
   264 
   264 
   265     
   265     
   266     # First check if configuration is OK.
   266     # First check if configuration is OK.
   267     if not _CheckWebConfiguration(newConfig):
   267     if not _CheckBacnetWebConfiguration(newConfig):
   268         return
   268         return
   269 
   269 
   270     # store to file the new configuration so that 
   270     # store to file the new configuration so that 
   271     # we can recoup the configuration the next time the PLC
   271     # we can recoup the configuration the next time the PLC
   272     # has a cold start (i.e. when Beremiz_service.py is retarted)
   272     # has a cold start (i.e. when Beremiz_service.py is retarted)
   273     _SetSavedConfiguration(newConfig)
   273     _SetBacnetSavedConfiguration(newConfig)
   274 
   274 
   275     # Configure PLC with the current BACnet parameters
   275     # Configure PLC with the current BACnet parameters
   276     _SetPLCConfiguration(newConfig)
   276     _SetBacnetPLCConfiguration(newConfig)
   277 
   277 
   278 
   278 
   279 
   279 
   280 def OnButtonReset(**kwargs):
   280 def OnBacnetButtonReset(**kwargs):
   281     """
   281     """
   282     # Function called when user clicks 'Delete' button in web interface
   282     # Function called when user clicks 'Delete' button in web interface
   283     # The function will delete the file containing the persistent
   283     # The function will delete the file containing the persistent
   284     # BACnet configution
   284     # BACnet configution
   285     """
   285     """
   286 
   286 
   287     # Delete the file
   287     # Delete the file
   288     _DelSavedConfiguration()
   288     _DelBacnetSavedConfiguration()
   289     # Set the current configuration to the default (hardcoded in C)
   289     # Set the current configuration to the default (hardcoded in C)
   290     _SetPLCConfiguration(_DefaultConfiguration)
   290     _SetBacnetPLCConfiguration(_BacnetDefaultConfiguration)
   291     # Reset global variable
   291     # Reset global variable
   292     global _SavedConfiguration
   292     global _BacnetSavedConfiguration
   293     _SavedConfiguration = None
   293     _BacnetSavedConfiguration = None
   294 
   294 
   295 
   295 
   296 
   296 
   297 # location_str is replaced by extension's value in CTNGenerateC call
   297 # location_str is replaced by extension's value in CTNGenerateC call
   298 def _runtime_bacnet_websettings_%(location_str)s_init():
   298 def _runtime_bacnet_websettings_%(location_str)s_init():
   315         GetParamFuncName = "__bacnet_%(location_str)s_get_ConfigParam_" + name
   315         GetParamFuncName = "__bacnet_%(location_str)s_get_ConfigParam_" + name
   316         SetParamFuncName = "__bacnet_%(location_str)s_set_ConfigParam_" + name
   316         SetParamFuncName = "__bacnet_%(location_str)s_set_ConfigParam_" + name
   317         
   317         
   318         # XXX TODO : stop reading from PLC .so file. This code is template code
   318         # XXX TODO : stop reading from PLC .so file. This code is template code
   319         #            that can use modbus extension build data
   319         #            that can use modbus extension build data
   320         GetParamFuncs[name]          = getattr(PLCObject.PLClibraryHandle, GetParamFuncName)
   320         BacnetGetParamFuncs[name]          = getattr(PLCObject.PLClibraryHandle, GetParamFuncName)
   321         GetParamFuncs[name].restype  = c_dtype
   321         BacnetGetParamFuncs[name].restype  = c_dtype
   322         GetParamFuncs[name].argtypes = None
   322         BacnetGetParamFuncs[name].argtypes = None
   323         
   323         
   324         SetParamFuncs[name]          = getattr(PLCObject.PLClibraryHandle, SetParamFuncName)
   324         BacnetSetParamFuncs[name]          = getattr(PLCObject.PLClibraryHandle, SetParamFuncName)
   325         SetParamFuncs[name].restype  = None
   325         BacnetSetParamFuncs[name].restype  = None
   326         SetParamFuncs[name].argtypes = [c_dtype]
   326         BacnetSetParamFuncs[name].argtypes = [c_dtype]
   327 
   327 
   328     # Default configuration is the configuration done in Beremiz IDE
   328     # Default configuration is the configuration done in Beremiz IDE
   329     # whose parameters get hardcoded into C, and compiled into the .so file
   329     # whose parameters get hardcoded into C, and compiled into the .so file
   330     # We read the default configuration from the .so file before the values
   330     # We read the default configuration from the .so file before the values
   331     # get changed by the user using the web server, or by the call (further on)
   331     # get changed by the user using the web server, or by the call (further on)
   332     # to _SetPLCConfiguration(SavedConfiguration)
   332     # to _SetBacnetPLCConfiguration(BacnetSavedConfiguration)
   333     global _DefaultConfiguration 
   333     global _BacnetDefaultConfiguration 
   334     _DefaultConfiguration = _GetPLCConfiguration()
   334     _BacnetDefaultConfiguration = _GetBacnetPLCConfiguration()
   335     
   335     
   336     # Show the current PLC configuration on the web interface        
   336     # Show the current PLC configuration on the web interface        
   337     global _WebviewConfiguration
   337     global _BacnetWebviewConfiguration
   338     _WebviewConfiguration = _GetPLCConfiguration()
   338     _BacnetWebviewConfiguration = _GetBacnetPLCConfiguration()
   339  
   339  
   340     # Read from file the last used configuration, which is likely
   340     # Read from file the last used configuration, which is likely
   341     # different to the hardcoded configuration.
   341     # different to the hardcoded configuration.
   342     # We Reset the current configuration (i.e., the config stored in the 
   342     # We Reset the current configuration (i.e., the config stored in the 
   343     # variables of .so file) to this saved configuration
   343     # variables of .so file) to this saved configuration
   344     # so the PLC will start off with this saved configuration instead
   344     # so the PLC will start off with this saved configuration instead
   345     # of the hardcoded (in Beremiz C generated code) configuration values.
   345     # of the hardcoded (in Beremiz C generated code) configuration values.
   346     #
   346     #
   347     # Note that _SetPLCConfiguration() will also update 
   347     # Note that _SetBacnetPLCConfiguration() will also update 
   348     # _WebviewConfiguration , if necessary.
   348     # _BacnetWebviewConfiguration , if necessary.
   349     global _SavedConfiguration
   349     global _BacnetSavedConfiguration
   350     _SavedConfiguration  = _GetSavedConfiguration()
   350     _BacnetSavedConfiguration  = _GetBacnetSavedConfiguration()
   351     if _SavedConfiguration is not None:
   351     if _BacnetSavedConfiguration is not None:
   352         if _CheckConfiguration(_SavedConfiguration):
   352         if _CheckBacnetConfiguration(_BacnetSavedConfiguration):
   353             _SetPLCConfiguration(_SavedConfiguration)
   353             _SetBacnetPLCConfiguration(_BacnetSavedConfiguration)
   354             
   354             
   355     WebSettings = NS.newExtensionSetting("BACnet extension", "bacnet_token")
   355     WebSettings = NS.newExtensionSetting("BACnet extension", "bacnet_token")
   356 
   356 
   357     # Configure the web interface to include the BACnet config parameters
   357     # Configure the web interface to include the BACnet config parameters
   358     WebSettings.addSettings(
   358     WebSettings.addSettings(
   359         "BACnetConfigParm",                # name
   359         "BACnetConfigParm",                # name
   360         _("BACnet Configuration"),         # description
   360         _("BACnet Configuration"),         # description
   361         webFormInterface,                  # fields
   361         webFormInterface,                  # fields
   362         _("Apply"),  # button label
   362         _("Apply"),  # button label
   363         OnButtonSave)                      # callback    
   363         OnBacnetButtonSave)                      # callback    
   364 
   364 
   365     # Add the Delete button to the web interface
   365     # Add the Delete button to the web interface
   366     WebSettings.addSettings(
   366     WebSettings.addSettings(
   367         "BACnetConfigDelSaved",                   # name
   367         "BACnetConfigDelSaved",                   # name
   368         _("BACnet Configuration"),                # description
   368         _("BACnet Configuration"),                # description
   369         [ ("status",
   369         [ ("status",
   370            annotate.String(label=_("Current state"),
   370            annotate.String(label=_("Current state"),
   371                            immutable=True,
   371                            immutable=True,
   372                            default=lambda *k:getConfigStatus())),
   372                            default=lambda *k:getBacnetConfigStatus())),
   373         ],                                       # fields  (empty, no parameters required!)
   373         ],                                       # fields  (empty, no parameters required!)
   374         _("Reset"), # button label
   374         _("Reset"), # button label
   375         OnButtonReset) 
   375         OnBacnetButtonReset) 
   376 
   376 
   377 
   377 
   378 
   378 
   379 def getConfigStatus():
   379 def getBacnetConfigStatus():
   380     if _WebviewConfiguration == _DefaultConfiguration :
   380     if _BacnetWebviewConfiguration == _BacnetDefaultConfiguration :
   381         return "Unchanged"
   381         return "Unchanged"
   382     return "Modified"
   382     return "Modified"
   383 
   383 
   384 
   384 
   385 # location_str is replaced by extension's value in CTNGenerateC call
   385 # location_str is replaced by extension's value in CTNGenerateC call
   390 
   390 
   391     #PLCObject.LogMessage("BACnet web server extension::OnUnLoadPLC() Called...")
   391     #PLCObject.LogMessage("BACnet web server extension::OnUnLoadPLC() Called...")
   392     
   392     
   393     NS.removeExtensionSetting("bacnet_token")
   393     NS.removeExtensionSetting("bacnet_token")
   394     
   394     
   395     GetParamFuncs = {}
   395     BacnetGetParamFuncs = {}
   396     SetParamFuncs = {}
   396     BacnetSetParamFuncs = {}
   397     _WebviewConfiguration = None
   397     _BacnetWebviewConfiguration = None
   398     _SavedConfiguration   = None
   398     _BacnetSavedConfiguration   = None
   399 
   399 
   400 
   400 
   401 
   401 
   402 
   402