113 # datazone = modbus_function_dict[function][5] |
115 # datazone = modbus_function_dict[function][5] |
114 # 'X' for bits, 'W' for words |
116 # 'X' for bits, 'W' for words |
115 datatacc = modbus_function_dict[function][6] |
117 datatacc = modbus_function_dict[function][6] |
116 # 'Coil', 'Holding Register', 'Input Discrete' or 'Input Register' |
118 # 'Coil', 'Holding Register', 'Input Discrete' or 'Input Register' |
117 dataname = modbus_function_dict[function][7] |
119 dataname = modbus_function_dict[function][7] |
|
120 # start off with a boolean entry |
|
121 # This is a flag used to allow the user program to control when to |
|
122 # execute the Modbus request. |
|
123 # NOTE: If the Modbus request has a 'current_location' of |
|
124 # %QX1.2.3 |
|
125 # then the execution control flag will be |
|
126 # %QX1.2.3.0.0 |
|
127 # and all the Modbus registers/coils will be |
|
128 # %QX1.2.3.0 |
|
129 # %QX1.2.3.1 |
|
130 # %QX1.2.3.2 |
|
131 # .. |
|
132 # %QX1.2.3.n |
118 entries = [] |
133 entries = [] |
|
134 entries.append({ |
|
135 "name": "Exec. request flag", |
|
136 "type": LOCATION_VAR_MEMORY, |
|
137 "size": 1, # BOOL flag |
|
138 "IEC_type": "BOOL", # BOOL flag |
|
139 "var_name": "var_name", |
|
140 "location": "X" + ".".join([str(i) for i in current_location]) + ".0.0", |
|
141 "description": "MB request execution control flag", |
|
142 "children": []}) |
119 for offset in range(address, address + count): |
143 for offset in range(address, address + count): |
120 entries.append({ |
144 entries.append({ |
121 "name": dataname + " " + str(offset), |
145 "name": dataname + " " + str(offset), |
122 "type": LOCATION_VAR_MEMORY, |
146 "type": LOCATION_VAR_MEMORY, |
123 "size": datasize, |
147 "size": datasize, |
258 # T C P C L I E N T # |
282 # T C P C L I E N T # |
259 # |
283 # |
260 # |
284 # |
261 # |
285 # |
262 |
286 |
|
287 # XXX TODO "Configuration_Name" should disapear in favor of CTN Name, which is already unique |
|
288 |
263 class _ModbusTCPclientPlug(object): |
289 class _ModbusTCPclientPlug(object): |
264 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
290 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
265 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
291 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
266 <xsd:element name="ModbusTCPclient"> |
292 <xsd:element name="ModbusTCPclient"> |
267 <xsd:complexType> |
293 <xsd:complexType> |
|
294 <xsd:attribute name="Configuration_Name" type="xsd:string" use="optional" default=""/> |
268 <xsd:attribute name="Remote_IP_Address" type="xsd:string" use="optional" default="localhost"/> |
295 <xsd:attribute name="Remote_IP_Address" type="xsd:string" use="optional" default="localhost"/> |
269 <xsd:attribute name="Remote_Port_Number" type="xsd:string" use="optional" default="502"/> |
296 <xsd:attribute name="Remote_Port_Number" type="xsd:string" use="optional" default="502"/> |
270 <xsd:attribute name="Invocation_Rate_in_ms" use="optional" default="100"> |
297 <xsd:attribute name="Invocation_Rate_in_ms" use="optional" default="100"> |
271 <xsd:simpleType> |
298 <xsd:simpleType> |
272 <xsd:restriction base="xsd:unsignedLong"> |
299 <xsd:restriction base="xsd:unsignedLong"> |
273 <xsd:minInclusive value="1"/> |
300 <xsd:minInclusive value="0"/> |
274 <xsd:maxInclusive value="2147483647"/> |
301 <xsd:maxInclusive value="2147483647"/> |
275 </xsd:restriction> |
302 </xsd:restriction> |
276 </xsd:simpleType> |
303 </xsd:simpleType> |
277 </xsd:attribute> |
304 </xsd:attribute> |
278 </xsd:complexType> |
305 </xsd:complexType> |
283 # corresponds to aprox 25 days. |
310 # corresponds to aprox 25 days. |
284 CTNChildrenTypes = [("ModbusRequest", _RequestPlug, "Request")] |
311 CTNChildrenTypes = [("ModbusRequest", _RequestPlug, "Request")] |
285 # TODO: Replace with CTNType !!! |
312 # TODO: Replace with CTNType !!! |
286 PlugType = "ModbusTCPclient" |
313 PlugType = "ModbusTCPclient" |
287 |
314 |
|
315 |
|
316 def __init__(self): |
|
317 # NOTE: |
|
318 # The ModbusTCPclient attribute is added dynamically by ConfigTreeNode._AddParamsMembers() |
|
319 # It will be an XML parser object created by |
|
320 # GenerateParserFromXSDstring(self.XSD).CreateRoot() |
|
321 |
|
322 # Set the default value for the "Configuration_Name" parameter |
|
323 # The default value will need to be different for each instance of the |
|
324 # _ModbusTCPclientPlug class, so we cannot hardcode the default value in the XSD above |
|
325 # This value will be used by the web interface |
|
326 # (i.e. the extension to the web server used to configure the Modbus parameters). |
|
327 # (The web server is run/activated/started by Beremiz_service.py) |
|
328 # (The web server code is found in runtime/NevowServer.py) |
|
329 # (The Modbus extension to the web server is found in runtime/Modbus_config.py) |
|
330 loc_str = ".".join(map(str, self.GetCurrentLocation())) |
|
331 self.ModbusTCPclient.setConfiguration_Name("Modbus TCP Client " + loc_str) |
|
332 |
288 # Return the number of (modbus library) nodes this specific TCP client will need |
333 # Return the number of (modbus library) nodes this specific TCP client will need |
289 # return type: (tcp nodes, rtu nodes, ascii nodes) |
334 # return type: (tcp nodes, rtu nodes, ascii nodes) |
290 def GetNodeCount(self): |
335 def GetNodeCount(self): |
291 return (1, 0, 0) |
336 return (1, 0, 0) |
|
337 |
|
338 def GetConfigName(self): |
|
339 """ Return the node's Configuration_Name """ |
|
340 return self.ModbusTCPclient.getConfiguration_Name() |
292 |
341 |
293 def CTNGenerate_C(self, buildpath, locations): |
342 def CTNGenerate_C(self, buildpath, locations): |
294 """ |
343 """ |
295 Generate C code |
344 Generate C code |
296 @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5) |
345 @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5) |
312 # T C P S E R V E R # |
361 # T C P S E R V E R # |
313 # |
362 # |
314 # |
363 # |
315 # |
364 # |
316 |
365 |
|
366 # XXX TODO "Configuration_Name" should disapear in favor of CTN Name, which is already unique |
|
367 |
317 class _ModbusTCPserverPlug(object): |
368 class _ModbusTCPserverPlug(object): |
318 # NOTE: the Port number is a 'string' and not an 'integer'! |
369 # NOTE: the Port number is a 'string' and not an 'integer'! |
319 # This is because the underlying modbus library accepts strings |
370 # This is because the underlying modbus library accepts strings |
320 # (e.g.: well known port names!) |
371 # (e.g.: well known port names!) |
321 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
372 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
322 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
373 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
323 <xsd:element name="ModbusServerNode"> |
374 <xsd:element name="ModbusServerNode"> |
324 <xsd:complexType> |
375 <xsd:complexType> |
|
376 <xsd:attribute name="Configuration_Name" type="xsd:string" use="optional" default=""/> |
325 <xsd:attribute name="Local_IP_Address" type="xsd:string" use="optional" default="#ANY#"/> |
377 <xsd:attribute name="Local_IP_Address" type="xsd:string" use="optional" default="#ANY#"/> |
326 <xsd:attribute name="Local_Port_Number" type="xsd:string" use="optional" default="502"/> |
378 <xsd:attribute name="Local_Port_Number" type="xsd:string" use="optional" default="502"/> |
327 <xsd:attribute name="SlaveID" use="optional" default="0"> |
379 <xsd:attribute name="SlaveID" use="optional" default="0"> |
328 <xsd:simpleType> |
380 <xsd:simpleType> |
329 <xsd:restriction base="xsd:integer"> |
381 <xsd:restriction base="xsd:integer"> |
338 """ |
390 """ |
339 CTNChildrenTypes = [("MemoryArea", _MemoryAreaPlug, "Memory Area")] |
391 CTNChildrenTypes = [("MemoryArea", _MemoryAreaPlug, "Memory Area")] |
340 # TODO: Replace with CTNType !!! |
392 # TODO: Replace with CTNType !!! |
341 PlugType = "ModbusTCPserver" |
393 PlugType = "ModbusTCPserver" |
342 |
394 |
|
395 def __init__(self): |
|
396 # NOTE: |
|
397 # The ModbusServerNode attribute is added dynamically by ConfigTreeNode._AddParamsMembers() |
|
398 # It will be an XML parser object created by |
|
399 # GenerateParserFromXSDstring(self.XSD).CreateRoot() |
|
400 |
|
401 # Set the default value for the "Configuration_Name" parameter |
|
402 # The default value will need to be different for each instance of the |
|
403 # _ModbusTCPclientPlug class, so we cannot hardcode the default value in the XSD above |
|
404 # This value will be used by the web interface |
|
405 # (i.e. the extension to the web server used to configure the Modbus parameters). |
|
406 # (The web server is run/activated/started by Beremiz_service.py) |
|
407 # (The web server code is found in runtime/NevowServer.py) |
|
408 # (The Modbus extension to the web server is found in runtime/Modbus_config.py) |
|
409 loc_str = ".".join(map(str, self.GetCurrentLocation())) |
|
410 self.ModbusServerNode.setConfiguration_Name("Modbus TCP Server " + loc_str) |
|
411 |
343 # Return the number of (modbus library) nodes this specific TCP server will need |
412 # Return the number of (modbus library) nodes this specific TCP server will need |
344 # return type: (tcp nodes, rtu nodes, ascii nodes) |
413 # return type: (tcp nodes, rtu nodes, ascii nodes) |
345 def GetNodeCount(self): |
414 def GetNodeCount(self): |
346 return (1, 0, 0) |
415 return (1, 0, 0) |
347 |
416 |
348 # Return a list with a single tuple conatining the (location, port number) |
417 # Return a list with a single tuple conatining the (location, IP address, port number) |
349 # location: location of this node in the configuration tree |
418 # location : location of this node in the configuration tree |
350 # port number: IP port used by this Modbus/IP server |
419 # port number: IP port used by this Modbus/IP server |
|
420 # IP address : IP address of the network interface on which the server will be listening |
|
421 # ("", "*", or "#ANY#" => listening on all interfaces!) |
351 def GetIPServerPortNumbers(self): |
422 def GetIPServerPortNumbers(self): |
352 port = self.GetParamsAttributes()[0]["children"][1]["value"] |
423 port = self.ModbusServerNode.getLocal_Port_Number() |
353 return [(self.GetCurrentLocation(), port)] |
424 addr = self.ModbusServerNode.getLocal_IP_Address() |
|
425 return [(self.GetCurrentLocation(), addr, port)] |
|
426 |
|
427 def GetConfigName(self): |
|
428 """ Return the node's Configuration_Name """ |
|
429 return self.ModbusServerNode.getConfiguration_Name() |
354 |
430 |
355 def CTNGenerate_C(self, buildpath, locations): |
431 def CTNGenerate_C(self, buildpath, locations): |
356 """ |
432 """ |
357 Generate C code |
433 Generate C code |
358 @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5) |
434 @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5) |
374 # R T U C L I E N T # |
450 # R T U C L I E N T # |
375 # |
451 # |
376 # |
452 # |
377 # |
453 # |
378 |
454 |
|
455 # XXX TODO "Configuration_Name" should disapear in favor of CTN Name, which is already unique |
|
456 |
379 class _ModbusRTUclientPlug(object): |
457 class _ModbusRTUclientPlug(object): |
380 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
458 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
381 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
459 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
382 <xsd:element name="ModbusRTUclient"> |
460 <xsd:element name="ModbusRTUclient"> |
383 <xsd:complexType> |
461 <xsd:complexType> |
|
462 <xsd:attribute name="Configuration_Name" type="xsd:string" use="optional" default=""/> |
384 <xsd:attribute name="Serial_Port" type="xsd:string" use="optional" default="/dev/ttyS0"/> |
463 <xsd:attribute name="Serial_Port" type="xsd:string" use="optional" default="/dev/ttyS0"/> |
385 <xsd:attribute name="Baud_Rate" type="xsd:string" use="optional" default="9600"/> |
464 <xsd:attribute name="Baud_Rate" type="xsd:string" use="optional" default="9600"/> |
386 <xsd:attribute name="Parity" type="xsd:string" use="optional" default="even"/> |
465 <xsd:attribute name="Parity" type="xsd:string" use="optional" default="even"/> |
387 <xsd:attribute name="Stop_Bits" type="xsd:string" use="optional" default="1"/> |
466 <xsd:attribute name="Stop_Bits" type="xsd:string" use="optional" default="1"/> |
388 <xsd:attribute name="Invocation_Rate_in_ms" use="optional" default="100"> |
467 <xsd:attribute name="Invocation_Rate_in_ms" use="optional" default="100"> |
389 <xsd:simpleType> |
468 <xsd:simpleType> |
390 <xsd:restriction base="xsd:integer"> |
469 <xsd:restriction base="xsd:integer"> |
391 <xsd:minInclusive value="1"/> |
470 <xsd:minInclusive value="0"/> |
392 <xsd:maxInclusive value="2147483647"/> |
471 <xsd:maxInclusive value="2147483647"/> |
393 </xsd:restriction> |
472 </xsd:restriction> |
394 </xsd:simpleType> |
473 </xsd:simpleType> |
395 </xsd:attribute> |
474 </xsd:attribute> |
396 </xsd:complexType> |
475 </xsd:complexType> |
401 # corresponds to aprox 25 days. |
480 # corresponds to aprox 25 days. |
402 CTNChildrenTypes = [("ModbusRequest", _RequestPlug, "Request")] |
481 CTNChildrenTypes = [("ModbusRequest", _RequestPlug, "Request")] |
403 # TODO: Replace with CTNType !!! |
482 # TODO: Replace with CTNType !!! |
404 PlugType = "ModbusRTUclient" |
483 PlugType = "ModbusRTUclient" |
405 |
484 |
|
485 def __init__(self): |
|
486 # NOTE: |
|
487 # The ModbusRTUclient attribute is added dynamically by ConfigTreeNode._AddParamsMembers() |
|
488 # It will be an XML parser object created by |
|
489 # GenerateParserFromXSDstring(self.XSD).CreateRoot() |
|
490 |
|
491 # Set the default value for the "Configuration_Name" parameter |
|
492 # The default value will need to be different for each instance of the |
|
493 # _ModbusTCPclientPlug class, so we cannot hardcode the default value in the XSD above |
|
494 # This value will be used by the web interface |
|
495 # (i.e. the extension to the web server used to configure the Modbus parameters). |
|
496 # (The web server is run/activated/started by Beremiz_service.py) |
|
497 # (The web server code is found in runtime/NevowServer.py) |
|
498 # (The Modbus extension to the web server is found in runtime/Modbus_config.py) |
|
499 loc_str = ".".join(map(str, self.GetCurrentLocation())) |
|
500 self.ModbusRTUclient.setConfiguration_Name("Modbus RTU Client " + loc_str) |
|
501 |
406 def GetParamsAttributes(self, path=None): |
502 def GetParamsAttributes(self, path=None): |
407 infos = ConfigTreeNode.GetParamsAttributes(self, path=path) |
503 infos = ConfigTreeNode.GetParamsAttributes(self, path=path) |
408 for element in infos: |
504 for element in infos: |
409 if element["name"] == "ModbusRTUclient": |
505 if element["name"] == "ModbusRTUclient": |
410 for child in element["children"]: |
506 for child in element["children"]: |
469 """ |
571 """ |
470 CTNChildrenTypes = [("MemoryArea", _MemoryAreaPlug, "Memory Area")] |
572 CTNChildrenTypes = [("MemoryArea", _MemoryAreaPlug, "Memory Area")] |
471 # TODO: Replace with CTNType !!! |
573 # TODO: Replace with CTNType !!! |
472 PlugType = "ModbusRTUslave" |
574 PlugType = "ModbusRTUslave" |
473 |
575 |
|
576 def __init__(self): |
|
577 # NOTE: |
|
578 # The ModbusRTUslave attribute is added dynamically by ConfigTreeNode._AddParamsMembers() |
|
579 # It will be an XML parser object created by |
|
580 # GenerateParserFromXSDstring(self.XSD).CreateRoot() |
|
581 |
|
582 # Set the default value for the "Configuration_Name" parameter |
|
583 # The default value will need to be different for each instance of the |
|
584 # _ModbusTCPclientPlug class, so we cannot hardcode the default value in the XSD above |
|
585 # This value will be used by the web interface |
|
586 # (i.e. the extension to the web server used to configure the Modbus parameters). |
|
587 # (The web server is run/activated/started by Beremiz_service.py) |
|
588 # (The web server code is found in runtime/NevowServer.py) |
|
589 # (The Modbus extension to the web server is found in runtime/Modbus_config.py) |
|
590 loc_str = ".".join(map(str, self.GetCurrentLocation())) |
|
591 self.ModbusRTUslave.setConfiguration_Name("Modbus RTU Slave " + loc_str) |
|
592 |
474 def GetParamsAttributes(self, path=None): |
593 def GetParamsAttributes(self, path=None): |
475 infos = ConfigTreeNode.GetParamsAttributes(self, path=path) |
594 infos = ConfigTreeNode.GetParamsAttributes(self, path=path) |
476 for element in infos: |
595 for element in infos: |
477 if element["name"] == "ModbusRTUslave": |
596 if element["name"] == "ModbusRTUslave": |
478 for child in element["children"]: |
597 for child in element["children"]: |
548 # ask each child how many nodes it needs, and add them all up. |
671 # ask each child how many nodes it needs, and add them all up. |
549 total_node_count = tuple( |
672 total_node_count = tuple( |
550 x1 + x2 for x1, x2 in zip(total_node_count, child.GetNodeCount())) |
673 x1 + x2 for x1, x2 in zip(total_node_count, child.GetNodeCount())) |
551 return total_node_count |
674 return total_node_count |
552 |
675 |
553 # Return a list with tuples of the (location, port numbers) used by all |
676 # Return a list with tuples of the (location, port numbers) used by all the Modbus/IP servers |
554 # the Modbus/IP servers |
|
555 def GetIPServerPortNumbers(self): |
677 def GetIPServerPortNumbers(self): |
556 IPServer_port_numbers = [] |
678 IPServer_port_numbers = [] |
557 for child in self.IECSortedChildren(): |
679 for child in self.IECSortedChildren(): |
558 if child.CTNType == "ModbusTCPserver": |
680 if child.CTNType == "ModbusTCPserver": |
559 IPServer_port_numbers.extend(child.GetIPServerPortNumbers()) |
681 IPServer_port_numbers.extend(child.GetIPServerPortNumbers()) |
560 return IPServer_port_numbers |
682 return IPServer_port_numbers |
|
683 |
|
684 # Return a list with tuples of the (location, configuration_name) used by all the Modbus nodes (tcp/rtu, clients/servers) |
|
685 def GetConfigNames(self): |
|
686 Node_Configuration_Names = [] |
|
687 for child in self.IECSortedChildren(): |
|
688 Node_Configuration_Names.extend([(child.GetCurrentLocation(), child.GetConfigName())]) |
|
689 return Node_Configuration_Names |
561 |
690 |
562 def CTNGenerate_C(self, buildpath, locations): |
691 def CTNGenerate_C(self, buildpath, locations): |
563 # print "#############" |
692 # print "#############" |
564 # print self.__class__ |
693 # print self.__class__ |
565 # print type(self) |
694 # print type(self) |
571 |
700 |
572 loc_dict = {"locstr": "_".join(map(str, self.GetCurrentLocation()))} |
701 loc_dict = {"locstr": "_".join(map(str, self.GetCurrentLocation()))} |
573 |
702 |
574 # Determine the number of (modbus library) nodes ALL instances of the modbus plugin will need |
703 # Determine the number of (modbus library) nodes ALL instances of the modbus plugin will need |
575 # total_node_count: (tcp nodes, rtu nodes, ascii nodes) |
704 # total_node_count: (tcp nodes, rtu nodes, ascii nodes) |
576 # Also get a list with tuples of (location, IP port numbers) used by all the Modbus/IP server nodes |
705 # |
|
706 # Also get a list with tuples of (location, IP address, port number) used by all the Modbus/IP server nodes |
577 # This list is later used to search for duplicates in port numbers! |
707 # This list is later used to search for duplicates in port numbers! |
578 # IPServer_port_numbers = [(location ,IPserver_port_number), ...] |
708 # IPServer_port_numbers = [(location, IP address, port number), ...] |
579 # location: tuple similar to (0, 3, 1) representing the location in the configuration tree "0.3.1.x" |
709 # location : tuple similar to (0, 3, 1) representing the location in the configuration tree "0.3.1.x" |
580 # IPserver_port_number: a number (i.e. port number used by the |
710 # IPserver_port_number: a number (i.e. port number used by the Modbus/IP server) |
581 # Modbus/IP server) |
711 # IP address : IP address of the network interface on which the server will be listening |
|
712 # ("", "*", or "#ANY#" => listening on all interfaces!) |
|
713 # |
|
714 # Also get a list with tuples of (location, Configuration_Name) used by all the Modbus nodes |
|
715 # This list is later used to search for duplicates in Configuration Names! |
|
716 # Node_Configuration_Names = [(location, Configuration_Name), ...] |
|
717 # location : tuple similar to (0, 3, 1) representing the location in the configuration tree "0.3.1.x" |
|
718 # Configuration_Name: the "Configuration_Name" string |
582 total_node_count = (0, 0, 0) |
719 total_node_count = (0, 0, 0) |
583 IPServer_port_numbers = [] |
720 IPServer_port_numbers = [] |
|
721 Node_Configuration_Names = [] |
584 for CTNInstance in self.GetCTRoot().IterChildren(): |
722 for CTNInstance in self.GetCTRoot().IterChildren(): |
585 if CTNInstance.CTNType == "modbus": |
723 if CTNInstance.CTNType == "modbus": |
586 # ask each modbus plugin instance how many nodes it needs, and |
724 # ask each modbus plugin instance how many nodes it needs, and add them all up. |
587 # add them all up. |
725 total_node_count = tuple(x1 + x2 for x1, x2 in zip(total_node_count, CTNInstance.GetNodeCount())) |
588 total_node_count = tuple(x1 + x2 for x1, x2 in zip( |
726 IPServer_port_numbers. extend(CTNInstance.GetIPServerPortNumbers()) |
589 total_node_count, CTNInstance.GetNodeCount())) |
727 Node_Configuration_Names.extend(CTNInstance.GetConfigNames ()) |
590 IPServer_port_numbers.extend( |
728 |
591 CTNInstance.GetIPServerPortNumbers()) |
729 # Search for use of duplicate Configuration_Names by Modbus nodes |
|
730 # Configuration Names are used by the web server running on the PLC |
|
731 # (more precisely, run by Beremiz_service.py) to identify and allow |
|
732 # changing the Modbus parameters after the program has been downloaded |
|
733 # to the PLC (but before it is started) |
|
734 # With clashes in the configuration names, the Modbus nodes will not be |
|
735 # distinguasheble on the web interface! |
|
736 for i in range(0, len(Node_Configuration_Names) - 1): |
|
737 for j in range(i + 1, len(Node_Configuration_Names)): |
|
738 if Node_Configuration_Names[i][1] == Node_Configuration_Names[j][1]: |
|
739 error_message = _("Error: Modbus plugin nodes %{a1}.x and %{a2}.x use the same Configuration_Name \"{a3}\".\n").format( |
|
740 a1=_lt_to_str(Node_Configuration_Names[i][0]), |
|
741 a2=_lt_to_str(Node_Configuration_Names[j][0]), |
|
742 a3=Node_Configuration_Names[j][1]) |
|
743 self.FatalError(error_message) |
592 |
744 |
593 # Search for use of duplicate port numbers by Modbus/IP servers |
745 # Search for use of duplicate port numbers by Modbus/IP servers |
594 # print IPServer_port_numbers |
746 # Note: We only consider duplicate port numbers if using the same network interface! |
595 # ..but first define a lambda function to convert a tuple with the config tree location to a nice looking string |
747 i = 0 |
596 # for e.g., convert the tuple (0, 3, 4) to "0.3.4" |
748 for loc1, addr1, port1 in IPServer_port_numbers[:-1]: |
597 |
749 i = i + 1 |
598 for i in range(0, len(IPServer_port_numbers) - 1): |
750 for loc2, addr2, port2 in IPServer_port_numbers[i:]: |
599 for j in range(i + 1, len(IPServer_port_numbers)): |
751 if (port1 == port2) and ( |
600 if IPServer_port_numbers[i][1] == IPServer_port_numbers[j][1]: |
752 (addr1 == addr2) # on the same network interface |
601 self.GetCTRoot().logger.write_warning( |
753 or (addr1 == "") or (addr1 == "*") or (addr1 == "#ANY#") # or one (or both) of the servers |
602 _("Error: Modbus/IP Servers %{a1}.x and %{a2}.x use the same port number {a3}.\n"). |
754 or (addr2 == "") or (addr2 == "*") or (addr2 == "#ANY#") # use all available network interfaces |
603 format( |
755 ): |
604 a1=_lt_to_str(IPServer_port_numbers[i][0]), |
756 error_message = _("Error: Modbus plugin nodes %{a1}.x and %{a2}.x use same port number \"{a3}\" " + |
605 a2=_lt_to_str(IPServer_port_numbers[j][0]), |
757 "on the same (or overlapping) network interfaces \"{a4}\" and \"{a5}\".\n").format( |
606 a3=IPServer_port_numbers[j][1])) |
758 a1=_lt_to_str(loc1), a2=_lt_to_str(loc2), a3=port1, a4=addr1, a5=addr2) |
607 raise Exception |
759 self.FatalError(error_message) |
608 # TODO: return an error code instead of raising an |
|
609 # exception |
|
610 |
760 |
611 # Determine the current location in Beremiz's project configuration |
761 # Determine the current location in Beremiz's project configuration |
612 # tree |
762 # tree |
613 current_location = self.GetCurrentLocation() |
763 current_location = self.GetCurrentLocation() |
614 |
764 |
718 return [], "", False |
868 return [], "", False |
719 client_request_list.append(new_req) |
869 client_request_list.append(new_req) |
720 for iecvar in subchild.GetLocations(): |
870 for iecvar in subchild.GetLocations(): |
721 # absloute address - start address |
871 # absloute address - start address |
722 relative_addr = iecvar["LOC"][3] - int(GetCTVal(subchild, 3)) |
872 relative_addr = iecvar["LOC"][3] - int(GetCTVal(subchild, 3)) |
723 # test if relative address in request specified range |
873 # test if the located variable |
724 if relative_addr in xrange(int(GetCTVal(subchild, 2))): |
874 # (a) has relative address in request specified range |
|
875 # AND is NOT |
|
876 # (b) is a control flag added by this modbus plugin |
|
877 # to control its execution at runtime. |
|
878 # Currently, we only add the "Execution Control Flag" |
|
879 # to each client request (one flag per request) |
|
880 # to control when to execute the request (if not executed periodically) |
|
881 # While all Modbus registers/coils are mapped onto a location |
|
882 # with 4 numbers (e.g. %QX0.1.2.55), this control flag is mapped |
|
883 # onto a location with 4 numbers (e.g. %QX0.1.2.0.0), where the last |
|
884 # two numbers are always '0.0', and the first two identify the request. |
|
885 # In the following if, we check for this condition by checking |
|
886 # if their are at least 4 or more number in the location's address. |
|
887 if ( relative_addr in xrange(int(GetCTVal(subchild, 2))) # condition (a) explained above |
|
888 and len(iecvar["LOC"]) < 5): # condition (b) explained above |
725 if str(iecvar["NAME"]) not in loc_vars_list: |
889 if str(iecvar["NAME"]) not in loc_vars_list: |
726 loc_vars.append( |
890 loc_vars.append( |
727 "u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr)) |
891 "u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr)) |
|
892 loc_vars_list.append(str(iecvar["NAME"])) |
|
893 # Now add the located variable in case it is a flag (condition (b) above |
|
894 if len(iecvar["LOC"]) >= 5: # condition (b) explained above |
|
895 if str(iecvar["NAME"]) not in loc_vars_list: |
|
896 loc_vars.append( |
|
897 "u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].flag_exec_req;" % (client_requestid)) |
728 loc_vars_list.append(str(iecvar["NAME"])) |
898 loc_vars_list.append(str(iecvar["NAME"])) |
729 client_requestid += 1 |
899 client_requestid += 1 |
730 tcpclient_node_count += 1 |
900 tcpclient_node_count += 1 |
731 client_nodeid += 1 |
901 client_nodeid += 1 |
732 # |
902 # |
743 return [], "", False |
913 return [], "", False |
744 client_request_list.append(new_req) |
914 client_request_list.append(new_req) |
745 for iecvar in subchild.GetLocations(): |
915 for iecvar in subchild.GetLocations(): |
746 # absloute address - start address |
916 # absloute address - start address |
747 relative_addr = iecvar["LOC"][3] - int(GetCTVal(subchild, 3)) |
917 relative_addr = iecvar["LOC"][3] - int(GetCTVal(subchild, 3)) |
748 # test if relative address in request specified range |
918 # test if the located variable |
749 if relative_addr in xrange(int(GetCTVal(subchild, 2))): |
919 # (a) has relative address in request specified range |
|
920 # AND is NOT |
|
921 # (b) is a control flag added by this modbus plugin |
|
922 # to control its execution at runtime. |
|
923 # Currently, we only add the "Execution Control Flag" |
|
924 # to each client request (one flag per request) |
|
925 # to control when to execute the request (if not executed periodically) |
|
926 # While all Modbus registers/coils are mapped onto a location |
|
927 # with 4 numbers (e.g. %QX0.1.2.55), this control flag is mapped |
|
928 # onto a location with 4 numbers (e.g. %QX0.1.2.0.0), where the last |
|
929 # two numbers are always '0.0', and the first two identify the request. |
|
930 # In the following if, we check for this condition by checking |
|
931 # if their are at least 4 or more number in the location's address. |
|
932 if ( relative_addr in xrange(int(GetCTVal(subchild, 2))) # condition (a) explained above |
|
933 and len(iecvar["LOC"]) < 5): # condition (b) explained above |
750 if str(iecvar["NAME"]) not in loc_vars_list: |
934 if str(iecvar["NAME"]) not in loc_vars_list: |
751 loc_vars.append( |
935 loc_vars.append( |
752 "u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr)) |
936 "u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr)) |
|
937 loc_vars_list.append(str(iecvar["NAME"])) |
|
938 # Now add the located variable in case it is a flag (condition (b) above |
|
939 if len(iecvar["LOC"]) >= 5: # condition (b) explained above |
|
940 if str(iecvar["NAME"]) not in loc_vars_list: |
|
941 loc_vars.append( |
|
942 "u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].flag_exec_req;" % (client_requestid)) |
753 loc_vars_list.append(str(iecvar["NAME"])) |
943 loc_vars_list.append(str(iecvar["NAME"])) |
754 client_requestid += 1 |
944 client_requestid += 1 |
755 rtuclient_node_count += 1 |
945 rtuclient_node_count += 1 |
756 client_nodeid += 1 |
946 client_nodeid += 1 |
757 nodeid += 1 |
947 nodeid += 1 |
801 # Target is ARM with linux and not win on x86 so winsock2 (ws2_32) library is useless !!! |
991 # Target is ARM with linux and not win on x86 so winsock2 (ws2_32) library is useless !!! |
802 # if os.name == 'nt': # other possible values: 'posix' 'os2' 'ce' 'java' 'riscos' |
992 # if os.name == 'nt': # other possible values: 'posix' 'os2' 'ce' 'java' 'riscos' |
803 # LDFLAGS.append(" -lws2_32 ") # on windows we need to load winsock |
993 # LDFLAGS.append(" -lws2_32 ") # on windows we need to load winsock |
804 # library! |
994 # library! |
805 |
995 |
806 return [(Gen_MB_c_path, ' -I"' + ModbusPath + '"')], LDFLAGS, True |
996 websettingfile = open(paths.AbsNeighbourFile(__file__, "web_settings.py"), 'r') |
|
997 websettingcode = websettingfile.read() |
|
998 websettingfile.close() |
|
999 |
|
1000 location_str = "_".join(map(str, self.GetCurrentLocation())) |
|
1001 websettingcode = websettingcode % locals() |
|
1002 |
|
1003 runtimefile_path = os.path.join(buildpath, "runtime_modbus_websettings.py") |
|
1004 runtimefile = open(runtimefile_path, 'w') |
|
1005 runtimefile.write(websettingcode) |
|
1006 runtimefile.close() |
|
1007 |
|
1008 return ([(Gen_MB_c_path, ' -I"' + ModbusPath + '"')], LDFLAGS, True, |
|
1009 ("runtime_modbus_websettings_%s.py" % location_str, open(runtimefile_path, "rb")), |
|
1010 ) |