modbus/web_settings.py
changeset 3743 5450dd9e9370
parent 3733 d1acf20e8e7c
child 3869 b722c800b6c7
equal deleted inserted replaced
3733:d1acf20e8e7c 3743:5450dd9e9370
   123                                  stringify = self.choice_to_label,
   123                                  stringify = self.choice_to_label,
   124                                  *args, **kwargs)
   124                                  *args, **kwargs)
   125 
   125 
   126 
   126 
   127 
   127 
       
   128 # The parameter coerce functions referenced in the TCPclient_parameters etc. lists.
       
   129 # These functions check wether the parameter has a legal value (i.e. they validate the parameter),
       
   130 # and if not changes it to the closest legal value (i.e. coerces the parameter value to a legal value)
       
   131 
       
   132 def _coerce_comm_period(value):
       
   133     # Period may not be negative value
       
   134     if (value < 0):
       
   135         value = 0
       
   136     return value
       
   137 
       
   138 def _coerce_req_delay(value):
       
   139     # Request delay may not be negative value
       
   140     # (i.e. delay between any two consecutive requests sent by a Beremiz RTU master or TCP client)
       
   141     if (value < 0):
       
   142         value = 0
       
   143     return value
       
   144 
       
   145 
       
   146 
   128 # Parameters we will need to get from the C code, but that will not be shown
   147 # Parameters we will need to get from the C code, but that will not be shown
   129 # on the web interface. Common to all modbus entry types (client/server, tcp/rtu/ascii)
   148 # on the web interface. Common to all modbus entry types (client/server, tcp/rtu/ascii)
   130 #
   149 #
   131 # The annotate type entry is basically useless and is completely ignored.
   150 # The annotate type entry is used to tell the web server the type of data entry widget to use.
   132 # We kee that entry so that this list can later be correctly merged with the
   151 # (see the file twisted/nevow/formless/annotate.py for list of options)
   133 # following lists...
   152 # Unfortunately an unsigned integer option does not exist.
   134 General_parameters = [
   153 General_parameters = [
   135     #    param. name       label                        ctype type         annotate type
   154     #    param. name       label                        ctype type         annotate type            Coerce
   136     # (C code var name)   (used on web interface)      (C data type)       (web data type)
   155     # (C code var name)   (used on web interface)      (C data type)       (web data type)          function
   137     #                                                                      (annotate.String,
   156     #                                                                      (annotate.String,
   138     #                                                                       annotate.Integer, ...)
   157     #                                                                       annotate.Integer, ...)
   139     ("config_name"      , _("")                      , ctypes.c_char_p,    annotate.String),
   158     ("config_name"      , _("")                      , ctypes.c_char_p,    annotate.String,         None),
   140     ("addr_type"        , _("")                      , ctypes.c_char_p,    annotate.String)
   159     ("addr_type"        , _("")                      , ctypes.c_char_p,    annotate.String,         None)
   141     ]                                                                      
   160     ]                                                                      
   142                                                                            
   161                                                                            
   143 # Parameters we will need to get from the C code, and that _will_ be shown
   162 # Parameters we will need to get from the C code, and that _will_ be shown
   144 # on the web interface.
   163 # on the web interface.
   145 TCPclient_parameters = [                                                   
   164 TCPclient_parameters = [                                                   
   146     #    param. name       label                        ctype type         annotate type
   165     #    param. name       label                        ctype type         annotate type            Coerce
   147     # (C code var name)   (used on web interface)      (C data type)       (web data type)
   166     # (C code var name)   (used on web interface)      (C data type)       (web data type)          function
   148     #                                                                      (annotate.String,
   167     #                                                                      (annotate.String,
   149     #                                                                       annotate.Integer, ...)
   168     #                                                                       annotate.Integer, ...)
   150     ("host"             , _("Remote IP Address")     , ctypes.c_char_p,    MB_StrippedString),
   169     ("host"             , _("Remote IP Address")     , ctypes.c_char_p,    MB_StrippedString,         None),
   151     ("port"             , _("Remote Port Number")    , ctypes.c_char_p,    MB_StrippedString),
   170     ("port"             , _("Remote Port Number")    , ctypes.c_char_p,    MB_StrippedString,         None),
   152     ("comm_period"      , _("Invocation Rate (ms)")  , ctypes.c_ulonglong, annotate.Integer ),
   171     ("comm_period"      , _("Invocation Rate (ms)")  , ctypes.c_ulonglong, annotate.Integer ,         _coerce_comm_period),
   153     ("req_delay"        , _("Request Delay (ms)")    , ctypes.c_ulonglong, annotate.Integer )
   172     ("req_delay"        , _("Request Delay (ms)")    , ctypes.c_ulonglong, annotate.Integer ,         _coerce_req_delay)
   154     ]
   173     ]
   155 
   174 
   156 RTUclient_parameters = [                                                   
   175 RTUclient_parameters = [                                                   
   157     #    param. name       label                        ctype type         annotate type
   176     #    param. name       label                        ctype type         annotate type            Coerce
   158     # (C code var name)   (used on web interface)      (C data type)       (web data type)
   177     # (C code var name)   (used on web interface)      (C data type)       (web data type)          function
   159     #                                                                      (annotate.String,
   178     #                                                                      (annotate.String,
   160     #                                                                       annotate.Integer, ...)
   179     #                                                                       annotate.Integer, ...)
   161     ("device"           , _("Serial Port")           , ctypes.c_char_p,    MB_StrippedString),
   180     ("device"           , _("Serial Port")           , ctypes.c_char_p,    MB_StrippedString,        None),
   162     ("baud"             , _("Baud Rate")             , ctypes.c_int,       MB_Baud         ),
   181     ("baud"             , _("Baud Rate")             , ctypes.c_int,       MB_Baud         ,         None),
   163     ("parity"           , _("Parity")                , ctypes.c_int,       MB_Parity       ),
   182     ("parity"           , _("Parity")                , ctypes.c_int,       MB_Parity       ,         None),
   164     ("stop_bits"        , _("Stop Bits")             , ctypes.c_int,       MB_StopBits     ),
   183     ("stop_bits"        , _("Stop Bits")             , ctypes.c_int,       MB_StopBits     ,         None),
   165     ("comm_period"      , _("Invocation Rate (ms)")  , ctypes.c_ulonglong, annotate.Integer),
   184     ("comm_period"      , _("Invocation Rate (ms)")  , ctypes.c_ulonglong, annotate.Integer,         _coerce_comm_period),
   166     ("req_delay"        , _("Request Delay (ms)")    , ctypes.c_ulonglong, annotate.Integer)
   185     ("req_delay"        , _("Request Delay (ms)")    , ctypes.c_ulonglong, annotate.Integer,         _coerce_req_delay)
   167     ]
   186     ]
   168 
   187 
   169 TCPserver_parameters = [                                                   
   188 TCPserver_parameters = [                                                   
   170     #    param. name       label                        ctype type         annotate type
   189     #    param. name       label                        ctype type         annotate type            Coerce
   171     # (C code var name)   (used on web interface)      (C data type)       (web data type)
   190     # (C code var name)   (used on web interface)      (C data type)       (web data type)          function
   172     #                                                                      (annotate.String,
   191     #                                                                      (annotate.String,
   173     #                                                                       annotate.Integer, ...)
   192     #                                                                       annotate.Integer, ...)
   174     ("host"             , _("Local IP Address")      , ctypes.c_char_p,    MB_StrippedString),
   193     ("host"             , _("Local IP Address")      , ctypes.c_char_p,    MB_StrippedString,         None),
   175     ("port"             , _("Local Port Number")     , ctypes.c_char_p,    MB_StrippedString),
   194     ("port"             , _("Local Port Number")     , ctypes.c_char_p,    MB_StrippedString,         None),
   176     ("slave_id"         , _("Slave ID")              , ctypes.c_ubyte,     annotate.Integer )
   195     ("slave_id"         , _("Slave ID")              , ctypes.c_ubyte,     annotate.Integer ,         None)
   177     ]
   196     ]
   178 
   197 
   179 RTUslave_parameters = [                                                   
   198 RTUslave_parameters = [                                                   
   180     #    param. name       label                        ctype type         annotate type
   199     #    param. name       label                        ctype type         annotate type            Coerce
   181     # (C code var name)   (used on web interface)      (C data type)       (web data type)
   200     # (C code var name)   (used on web interface)      (C data type)       (web data type)          function
   182     #                                                                      (annotate.String,
   201     #                                                                      (annotate.String,
   183     #                                                                       annotate.Integer, ...)
   202     #                                                                       annotate.Integer, ...)
   184     ("device"           , _("Serial Port")           , ctypes.c_char_p,    MB_StrippedString),
   203     ("device"           , _("Serial Port")           , ctypes.c_char_p,    MB_StrippedString,        None),
   185     ("baud"             , _("Baud Rate")             , ctypes.c_int,       MB_Baud         ),
   204     ("baud"             , _("Baud Rate")             , ctypes.c_int,       MB_Baud         ,         None),
   186     ("parity"           , _("Parity")                , ctypes.c_int,       MB_Parity       ),
   205     ("parity"           , _("Parity")                , ctypes.c_int,       MB_Parity       ,         None),
   187     ("stop_bits"        , _("Stop Bits")             , ctypes.c_int,       MB_StopBits     ),
   206     ("stop_bits"        , _("Stop Bits")             , ctypes.c_int,       MB_StopBits     ,         None),
   188     ("slave_id"         , _("Slave ID")              , ctypes.c_ubyte,     annotate.Integer)
   207     ("slave_id"         , _("Slave ID")              , ctypes.c_ubyte,     annotate.Integer,         None)
   189     ]
   208     ]
   190 
   209 
   191 
   210 
   192 
   211 
   193 
   212 
   211 WebParamListDictDict['server'] = _server_WebParamListDict
   230 WebParamListDictDict['server'] = _server_WebParamListDict
   212 
   231 
   213 
   232 
   214 
   233 
   215 
   234 
   216 
       
   217 
       
   218 def _SetModbusSavedConfiguration(WebNode_id, newConfig):
   235 def _SetModbusSavedConfiguration(WebNode_id, newConfig):
   219     """ Stores a dictionary in a persistant file containing the Modbus parameter configuration """
   236     """ Stores a dictionary in a persistant file containing the Modbus parameter configuration """
   220     WebNode_entry = _WebNodeList[WebNode_id]
   237     WebNode_entry = _WebNodeList[WebNode_id]
   221 
   238 
   222     if WebNode_entry["DefaultConfiguration"] == newConfig:
   239     if WebNode_entry["DefaultConfiguration"] == newConfig:
   294     current_config = {}
   311     current_config = {}
   295     C_node_id      = _WebNodeList[WebNode_id]["C_node_id"]
   312     C_node_id      = _WebNodeList[WebNode_id]["C_node_id"]
   296     WebParamList   = _WebNodeList[WebNode_id]["WebParamList"]
   313     WebParamList   = _WebNodeList[WebNode_id]["WebParamList"]
   297     GetParamFuncs  = _WebNodeList[WebNode_id]["GetParamFuncs"]
   314     GetParamFuncs  = _WebNodeList[WebNode_id]["GetParamFuncs"]
   298 
   315 
   299     for par_name, x1, x2, x3 in WebParamList:
   316     for WebParamListEntry in WebParamList:
       
   317         par_name = WebParamListEntry[0]
   300         value = GetParamFuncs[par_name](C_node_id)
   318         value = GetParamFuncs[par_name](C_node_id)
   301         if value is not None:
   319         if value is not None:
   302             current_config[par_name] = value
   320             current_config[par_name] = value
   303     
   321     
   304     return current_config
   322     return current_config
   334         return _WebNodeList[WebNode_id]["WebviewConfiguration"][argument.name]
   352         return _WebNodeList[WebNode_id]["WebviewConfiguration"][argument.name]
   335     except Exception:
   353     except Exception:
   336         return ""
   354         return ""
   337 
   355 
   338 
   356 
       
   357 def _CoerceConfigurationValues(WebNode_id, config):
       
   358     """
       
   359     Coerces any incorrect value in the passed configuration
       
   360     to the nearest correct value.
       
   361     For example, negative times in delay paramater are changed to 0, etc...
       
   362     """
       
   363     CoerceParamFuncs  = _WebNodeList[WebNode_id]["CoerceParamFuncs"]
       
   364 
       
   365     for par_name in config:
       
   366         CoerceFunction = CoerceParamFuncs[par_name]
       
   367         if (config[par_name] is not None) and (CoerceFunction is not None):
       
   368             config[par_name] = CoerceFunction(config[par_name])
       
   369 
       
   370 
   339 
   371 
   340 def OnModbusButtonSave(**kwargs):
   372 def OnModbusButtonSave(**kwargs):
   341     """
   373     """
   342     Function called when user clicks 'Save' button in web interface
   374     Function called when user clicks 'Save' button in web interface
   343     The function will configure the Modbus plugin in the PLC with the values
   375     The function will configure the Modbus plugin in the PLC with the values
   352     
   384     
   353     newConfig    = {}
   385     newConfig    = {}
   354     WebNode_id   =  kwargs.get("WebNode_id", None)
   386     WebNode_id   =  kwargs.get("WebNode_id", None)
   355     WebParamList = _WebNodeList[WebNode_id]["WebParamList"]
   387     WebParamList = _WebNodeList[WebNode_id]["WebParamList"]
   356     
   388     
   357     for par_name, x1, x2, x3 in WebParamList:
   389     for WebParamListEntry in WebParamList:
       
   390         par_name = WebParamListEntry[0]
   358         value = kwargs.get(par_name, None)
   391         value = kwargs.get(par_name, None)
   359         if value is not None:
   392         if value is not None:
   360             newConfig[par_name] = value
   393             newConfig[par_name] = value
   361 
   394 
   362     # First check if configuration is OK.
   395     # First check if configuration is OK.
   363     # Note that this is not currently required, as we use drop down choice menus
   396     # Note that this is currently not really required for most of the parameters,
   364     # for baud, parity and sop bits, so the values should always be correct!
   397     # as we use drop down choice menus for baud, parity and sop bits
   365     #if not _CheckWebConfiguration(newConfig):
   398     # (so these values should always be correct!).
   366     #    return
   399     # The timing properties (period in ms, delays in ms, etc.)
       
   400     # do however need to be validated as the web interface does not
       
   401     # enforce any limits on the values (e.g., they must be positive values).
       
   402     # NOTE: It would be confusing for the user if we simply refuse to make the
       
   403     # requested changes without giving any feedback that the changes were not applied,
       
   404     # and why they were refused (i.e. at the moment the web page does not have
       
   405     # any pop-up windows or special messages for that).
       
   406     # That is why instead of returning without making the changes in case of error,
       
   407     # we instead opt to change the requested configuration to the closest valid values
       
   408     # and apply that instead. The user gets feedback on the applied values as these get
       
   409     # shown on the web interface.
       
   410     #
       
   411     # TODO: Check IP address, Port number, etc...
       
   412     _CoerceConfigurationValues(WebNode_id, newConfig)
   367     
   413     
   368     # store to file the new configuration so that 
   414     # store to file the new configuration so that 
   369     # we can recoup the configuration the next time the PLC
   415     # we can recoup the configuration the next time the PLC
   370     # has a cold start (i.e. when Beremiz_service.py is retarted)
   416     # has a cold start (i.e. when Beremiz_service.py is retarted)
   371     _SetModbusSavedConfiguration(WebNode_id, newConfig)
   417     _SetModbusSavedConfiguration(WebNode_id, newConfig)
   380 
   426 
   381 
   427 
   382 
   428 
   383 def OnModbusButtonReset(**kwargs):
   429 def OnModbusButtonReset(**kwargs):
   384     """
   430     """
   385     Function called when user clicks 'Delete' button in web interface
   431     Function called when user clicks 'Reset' button in web interface
   386     The function will delete the file containing the persistent
   432     The function will delete the file containing the persistent
   387     Modbus configution
   433     Modbus configution. More specifically, reset to the configuration
       
   434     hardcoded in the source code (<=> configuration specified
       
   435     in Beremiz IDE).
   388     """
   436     """
   389 
   437 
   390     WebNode_id = kwargs.get("WebNode_id", None)
   438     WebNode_id = kwargs.get("WebNode_id", None)
   391     
   439     
   392     # Delete the file
   440     # Delete the file
   404     
   452     
   405 
   453 
   406 
   454 
   407 
   455 
   408 
   456 
   409 def _AddWebNode(C_node_id, node_type, GetParamFuncs, SetParamFuncs):
   457 def _AddWebNode(C_node_id, node_type, GetParamFuncs, SetParamFuncs, CoerceParamFuncs):
   410     """
   458     """
   411     Load from the compiled code (.so file, aloready loaded into memmory)
   459     Load from the compiled code (.so file, aloready loaded into memmory)
   412     the configuration parameters of a specific Modbus plugin node.
   460     the configuration parameters of a specific Modbus plugin node.
   413     This function works with both client and server nodes, depending on the
   461     This function works with both client and server nodes, depending on the
   414     Get/SetParamFunc dictionaries passed to it (either the client or the server
   462     Get/SetParamFunc dictionaries passed to it (either the client or the server
   441     #  - TCPclient_parameters, TCPserver_parameters, RTUclient_parameters, RTUslave_parameters
   489     #  - TCPclient_parameters, TCPserver_parameters, RTUclient_parameters, RTUslave_parameters
   442     WebNode_entry["C_node_id"    ] = C_node_id
   490     WebNode_entry["C_node_id"    ] = C_node_id
   443     WebNode_entry["config_name"  ] = config_name 
   491     WebNode_entry["config_name"  ] = config_name 
   444     WebNode_entry["config_hash"  ] = config_hash
   492     WebNode_entry["config_hash"  ] = config_hash
   445     WebNode_entry["filename"     ] = os.path.join(_ModbusConfFiledir, "Modbus_config_" + config_hash + ".json")
   493     WebNode_entry["filename"     ] = os.path.join(_ModbusConfFiledir, "Modbus_config_" + config_hash + ".json")
   446     WebNode_entry["GetParamFuncs"] = GetParamFuncs
       
   447     WebNode_entry["SetParamFuncs"] = SetParamFuncs
       
   448     WebNode_entry["WebParamList" ] = WebParamListDictDict[node_type][addr_type] 
   494     WebNode_entry["WebParamList" ] = WebParamListDictDict[node_type][addr_type] 
   449     WebNode_entry["addr_type"    ] = addr_type  # 'tcp', 'rtu', or 'ascii' (as returned by C function)
   495     WebNode_entry["addr_type"    ] = addr_type  # 'tcp', 'rtu', or 'ascii' (as returned by C function)
   450     WebNode_entry["node_type"    ] = node_type  # 'client', 'server'
   496     WebNode_entry["node_type"    ] = node_type  # 'client', 'server'
       
   497     WebNode_entry["GetParamFuncs"] = GetParamFuncs
       
   498     WebNode_entry["SetParamFuncs"] = SetParamFuncs
       
   499     WebNode_entry["CoerceParamFuncs"] = CoerceParamFuncs
   451         
   500         
   452     
   501     
   453     # Dictionary that contains the Modbus configuration currently being shown
   502     # Dictionary that contains the Modbus configuration currently being shown
   454     # on the web interface
   503     # on the web interface
   455     # This configuration will almost always be identical to the current
   504     # This configuration will almost always be identical to the current
   489     #           by the set_***() C functions in mb_runtime.c
   538     #           by the set_***() C functions in mb_runtime.c
   490     def __GetWebviewConfigurationValue(ctx, argument):
   539     def __GetWebviewConfigurationValue(ctx, argument):
   491         return str(_GetModbusWebviewConfigurationValue(ctx, WebNode_id, argument))
   540         return str(_GetModbusWebviewConfigurationValue(ctx, WebNode_id, argument))
   492     
   541     
   493     webFormInterface = [(name, web_dtype (label=web_label, default=__GetWebviewConfigurationValue)) 
   542     webFormInterface = [(name, web_dtype (label=web_label, default=__GetWebviewConfigurationValue)) 
   494                     for name, web_label, c_dtype, web_dtype in WebNode_entry["WebParamList"]]
   543                     for name, web_label, c_dtype, web_dtype, coerce_function in WebNode_entry["WebParamList"]]
   495 
   544 
   496     # Configure the web interface to include the Modbus config parameters
   545     # Configure the web interface to include the Modbus config parameters
   497     def __OnButtonSave(**kwargs):
   546     def __OnButtonSave(**kwargs):
   498         OnModbusButtonSave(WebNode_id=WebNode_id, **kwargs)
   547         OnModbusButtonSave(WebNode_id=WebNode_id, **kwargs)
   499 
   548 
   567         return
   616         return
   568     
   617     
   569     # Map the get/set functions (written in C code) we will be using to get/set the configuration parameters
   618     # Map the get/set functions (written in C code) we will be using to get/set the configuration parameters
   570     # Will contain references to the C functions (implemented in beremiz/modbus/mb_runtime.c)
   619     # Will contain references to the C functions (implemented in beremiz/modbus/mb_runtime.c)
   571     GetClientParamFuncs = {}
   620     GetClientParamFuncs = {}
       
   621     GetServerParamFuncs = {}
   572     SetClientParamFuncs = {}
   622     SetClientParamFuncs = {}
   573     GetServerParamFuncs = {}
       
   574     SetServerParamFuncs = {}
   623     SetServerParamFuncs = {}
       
   624     CoerceClientParamFuncs = {}
       
   625     CoerceServerParamFuncs = {}
   575 
   626 
   576     # XXX TODO : stop reading from PLC .so file. This code is template code
   627     # XXX TODO : stop reading from PLC .so file. This code is template code
   577     #            that can use modbus extension build data
   628     #            that can use modbus extension build data
   578     for name, web_label, c_dtype, web_dtype in TCPclient_parameters + RTUclient_parameters + General_parameters:
   629     for name, web_label, c_dtype, web_dtype, coerce_function in TCPclient_parameters + RTUclient_parameters + General_parameters:
   579         ParamFuncName                      = "__modbus_get_ClientNode_" + name        
   630         ParamFuncName                      = "__modbus_get_ClientNode_" + name        
   580         GetClientParamFuncs[name]          = getattr(PLCObject.PLClibraryHandle, ParamFuncName)
   631         GetClientParamFuncs[name]          = getattr(PLCObject.PLClibraryHandle, ParamFuncName)
   581         GetClientParamFuncs[name].restype  = c_dtype
   632         GetClientParamFuncs[name].restype  = c_dtype
   582         GetClientParamFuncs[name].argtypes = [ctypes.c_int]
   633         GetClientParamFuncs[name].argtypes = [ctypes.c_int]
   583         
   634         
   584     for name, web_label, c_dtype, web_dtype in TCPclient_parameters + RTUclient_parameters:
   635     for name, web_label, c_dtype, web_dtype, coerce_function in TCPclient_parameters + RTUclient_parameters:
   585         ParamFuncName                      = "__modbus_set_ClientNode_" + name
   636         ParamFuncName                      = "__modbus_set_ClientNode_" + name
   586         SetClientParamFuncs[name]          = getattr(PLCObject.PLClibraryHandle, ParamFuncName)
   637         SetClientParamFuncs[name]          = getattr(PLCObject.PLClibraryHandle, ParamFuncName)
   587         SetClientParamFuncs[name].restype  = None
   638         SetClientParamFuncs[name].restype  = None
   588         SetClientParamFuncs[name].argtypes = [ctypes.c_int, c_dtype]
   639         SetClientParamFuncs[name].argtypes = [ctypes.c_int, c_dtype]
       
   640         CoerceClientParamFuncs[name]       = coerce_function
   589 
   641 
   590     # XXX TODO : stop reading from PLC .so file. This code is template code
   642     # XXX TODO : stop reading from PLC .so file. This code is template code
   591     #            that can use modbus extension build data
   643     #            that can use modbus extension build data
   592     for name, web_label, c_dtype, web_dtype in TCPserver_parameters + RTUslave_parameters + General_parameters:
   644     for name, web_label, c_dtype, web_dtype, coerce_function in TCPserver_parameters + RTUslave_parameters + General_parameters:
   593         ParamFuncName                      = "__modbus_get_ServerNode_" + name        
   645         ParamFuncName                      = "__modbus_get_ServerNode_" + name        
   594         GetServerParamFuncs[name]          = getattr(PLCObject.PLClibraryHandle, ParamFuncName)
   646         GetServerParamFuncs[name]          = getattr(PLCObject.PLClibraryHandle, ParamFuncName)
   595         GetServerParamFuncs[name].restype  = c_dtype
   647         GetServerParamFuncs[name].restype  = c_dtype
   596         GetServerParamFuncs[name].argtypes = [ctypes.c_int]
   648         GetServerParamFuncs[name].argtypes = [ctypes.c_int]
   597         
   649         
   598     for name, web_label, c_dtype, web_dtype in TCPserver_parameters + RTUslave_parameters:
   650     for name, web_label, c_dtype, web_dtype, coerce_function in TCPserver_parameters + RTUslave_parameters:
   599         ParamFuncName                      = "__modbus_set_ServerNode_" + name
   651         ParamFuncName                      = "__modbus_set_ServerNode_" + name
   600         SetServerParamFuncs[name]          = getattr(PLCObject.PLClibraryHandle, ParamFuncName)
   652         SetServerParamFuncs[name]          = getattr(PLCObject.PLClibraryHandle, ParamFuncName)
   601         SetServerParamFuncs[name].restype  = None
   653         SetServerParamFuncs[name].restype  = None
   602         SetServerParamFuncs[name].argtypes = [ctypes.c_int, c_dtype]
   654         SetServerParamFuncs[name].argtypes = [ctypes.c_int, c_dtype]
       
   655         CoerceServerParamFuncs[name]       = coerce_function
   603 
   656 
   604     for node_id in range(client_count):
   657     for node_id in range(client_count):
   605         _AddWebNode(node_id, "client" ,GetClientParamFuncs, SetClientParamFuncs)
   658         _AddWebNode(node_id, "client" ,GetClientParamFuncs, SetClientParamFuncs, CoerceClientParamFuncs)
   606 
   659 
   607     for node_id in range(server_count):
   660     for node_id in range(server_count):
   608         _AddWebNode(node_id, "server", GetServerParamFuncs, SetServerParamFuncs)
   661         _AddWebNode(node_id, "server", GetServerParamFuncs, SetServerParamFuncs, CoerceServerParamFuncs)
   609 
   662 
   610 
   663 
   611 
   664 
   612 
   665 
   613 
   666