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 |
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) |
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 |