403 # Return the number of (modbus library) nodes this specific TCP server will need |
407 # Return the number of (modbus library) nodes this specific TCP server will need |
404 # return type: (tcp nodes, rtu nodes, ascii nodes) |
408 # return type: (tcp nodes, rtu nodes, ascii nodes) |
405 def GetNodeCount(self): |
409 def GetNodeCount(self): |
406 return (1, 0, 0) |
410 return (1, 0, 0) |
407 |
411 |
408 # Return a list with a single tuple conatining the (location, port number) |
412 # Return a list with a single tuple conatining the (location, IP address, port number) |
409 # location: location of this node in the configuration tree |
413 # location : location of this node in the configuration tree |
410 # port number: IP port used by this Modbus/IP server |
414 # port number: IP port used by this Modbus/IP server |
|
415 # IP address : IP address of the network interface on which the server will be listening |
|
416 # ("", "*", or "#ANY#" => listening on all interfaces!) |
411 def GetIPServerPortNumbers(self): |
417 def GetIPServerPortNumbers(self): |
412 port = self.GetParamsAttributes()[0]["children"][1]["value"] |
418 port = self.ModbusServerNode.getLocal_Port_Number() |
413 return [(self.GetCurrentLocation(), port)] |
419 addr = self.ModbusServerNode.getLocal_IP_Address() |
|
420 return [(self.GetCurrentLocation(), addr, port)] |
|
421 |
|
422 def GetConfigName(self): |
|
423 """ Return the node's Configuration_Name """ |
|
424 return self.ModbusServerNode.getConfiguration_Name() |
414 |
425 |
415 def CTNGenerate_C(self, buildpath, locations): |
426 def CTNGenerate_C(self, buildpath, locations): |
416 """ |
427 """ |
417 Generate C code |
428 Generate C code |
418 @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5) |
429 @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5) |
644 # ask each child how many nodes it needs, and add them all up. |
663 # ask each child how many nodes it needs, and add them all up. |
645 total_node_count = tuple( |
664 total_node_count = tuple( |
646 x1 + x2 for x1, x2 in zip(total_node_count, child.GetNodeCount())) |
665 x1 + x2 for x1, x2 in zip(total_node_count, child.GetNodeCount())) |
647 return total_node_count |
666 return total_node_count |
648 |
667 |
649 # Return a list with tuples of the (location, port numbers) used by all |
668 # Return a list with tuples of the (location, port numbers) used by all the Modbus/IP servers |
650 # the Modbus/IP servers |
|
651 def GetIPServerPortNumbers(self): |
669 def GetIPServerPortNumbers(self): |
652 IPServer_port_numbers = [] |
670 IPServer_port_numbers = [] |
653 for child in self.IECSortedChildren(): |
671 for child in self.IECSortedChildren(): |
654 if child.CTNType == "ModbusTCPserver": |
672 if child.CTNType == "ModbusTCPserver": |
655 IPServer_port_numbers.extend(child.GetIPServerPortNumbers()) |
673 IPServer_port_numbers.extend(child.GetIPServerPortNumbers()) |
656 return IPServer_port_numbers |
674 return IPServer_port_numbers |
|
675 |
|
676 # Return a list with tuples of the (location, configuration_name) used by all the Modbus nodes (tcp/rtu, clients/servers) |
|
677 def GetConfigNames(self): |
|
678 Node_Configuration_Names = [] |
|
679 for child in self.IECSortedChildren(): |
|
680 Node_Configuration_Names.extend([(child.GetCurrentLocation(), child.GetConfigName())]) |
|
681 return Node_Configuration_Names |
657 |
682 |
658 def CTNGenerate_C(self, buildpath, locations): |
683 def CTNGenerate_C(self, buildpath, locations): |
659 # print "#############" |
684 # print "#############" |
660 # print self.__class__ |
685 # print self.__class__ |
661 # print type(self) |
686 # print type(self) |
667 |
692 |
668 loc_dict = {"locstr": "_".join(map(str, self.GetCurrentLocation()))} |
693 loc_dict = {"locstr": "_".join(map(str, self.GetCurrentLocation()))} |
669 |
694 |
670 # Determine the number of (modbus library) nodes ALL instances of the modbus plugin will need |
695 # Determine the number of (modbus library) nodes ALL instances of the modbus plugin will need |
671 # total_node_count: (tcp nodes, rtu nodes, ascii nodes) |
696 # total_node_count: (tcp nodes, rtu nodes, ascii nodes) |
672 # Also get a list with tuples of (location, IP port numbers) used by all the Modbus/IP server nodes |
697 # |
|
698 # Also get a list with tuples of (location, IP address, port number) used by all the Modbus/IP server nodes |
673 # This list is later used to search for duplicates in port numbers! |
699 # This list is later used to search for duplicates in port numbers! |
674 # IPServer_port_numbers = [(location ,IPserver_port_number), ...] |
700 # IPServer_port_numbers = [(location, IP address, port number), ...] |
675 # location: tuple similar to (0, 3, 1) representing the location in the configuration tree "0.3.1.x" |
701 # location : tuple similar to (0, 3, 1) representing the location in the configuration tree "0.3.1.x" |
676 # IPserver_port_number: a number (i.e. port number used by the |
702 # IPserver_port_number: a number (i.e. port number used by the Modbus/IP server) |
677 # Modbus/IP server) |
703 # IP address : IP address of the network interface on which the server will be listening |
|
704 # ("", "*", or "#ANY#" => listening on all interfaces!) |
|
705 # |
|
706 # Also get a list with tuples of (location, Configuration_Name) used by all the Modbus nodes |
|
707 # This list is later used to search for duplicates in Configuration Names! |
|
708 # Node_Configuration_Names = [(location, Configuration_Name), ...] |
|
709 # location : tuple similar to (0, 3, 1) representing the location in the configuration tree "0.3.1.x" |
|
710 # Configuration_Name: the "Configuration_Name" string |
678 total_node_count = (0, 0, 0) |
711 total_node_count = (0, 0, 0) |
679 IPServer_port_numbers = [] |
712 IPServer_port_numbers = [] |
|
713 Node_Configuration_Names = [] |
680 for CTNInstance in self.GetCTRoot().IterChildren(): |
714 for CTNInstance in self.GetCTRoot().IterChildren(): |
681 if CTNInstance.CTNType == "modbus": |
715 if CTNInstance.CTNType == "modbus": |
682 # ask each modbus plugin instance how many nodes it needs, and |
716 # ask each modbus plugin instance how many nodes it needs, and add them all up. |
683 # add them all up. |
717 total_node_count = tuple(x1 + x2 for x1, x2 in zip(total_node_count, CTNInstance.GetNodeCount())) |
684 total_node_count = tuple(x1 + x2 for x1, x2 in zip( |
718 IPServer_port_numbers. extend(CTNInstance.GetIPServerPortNumbers()) |
685 total_node_count, CTNInstance.GetNodeCount())) |
719 Node_Configuration_Names.extend(CTNInstance.GetConfigNames ()) |
686 IPServer_port_numbers.extend( |
720 |
687 CTNInstance.GetIPServerPortNumbers()) |
721 # Search for use of duplicate Configuration_Names by Modbus nodes |
|
722 # Configuration Names are used by the web server running on the PLC |
|
723 # (more precisely, run by Beremiz_service.py) to identify and allow |
|
724 # changing the Modbus parameters after the program has been downloaded |
|
725 # to the PLC (but before it is started) |
|
726 # With clashes in the configuration names, the Modbus nodes will not be |
|
727 # distinguasheble on the web interface! |
|
728 for i in range(0, len(Node_Configuration_Names) - 1): |
|
729 for j in range(i + 1, len(Node_Configuration_Names)): |
|
730 if Node_Configuration_Names[i][1] == Node_Configuration_Names[j][1]: |
|
731 error_message = _("Error: Modbus plugin nodes %{a1}.x and %{a2}.x use the same Configuration_Name \"{a3}\".\n").format( |
|
732 a1=_lt_to_str(Node_Configuration_Names[i][0]), |
|
733 a2=_lt_to_str(Node_Configuration_Names[j][0]), |
|
734 a3=Node_Configuration_Names[j][1]) |
|
735 self.FatalError(error_message) |
688 |
736 |
689 # Search for use of duplicate port numbers by Modbus/IP servers |
737 # Search for use of duplicate port numbers by Modbus/IP servers |
690 # print IPServer_port_numbers |
738 # Note: We only consider duplicate port numbers if using the same network interface! |
691 # ..but first define a lambda function to convert a tuple with the config tree location to a nice looking string |
739 i = 0 |
692 # for e.g., convert the tuple (0, 3, 4) to "0.3.4" |
740 for loc1, addr1, port1 in IPServer_port_numbers[:-1]: |
693 |
741 i = i + 1 |
694 for i in range(0, len(IPServer_port_numbers) - 1): |
742 for loc2, addr2, port2 in IPServer_port_numbers[i:]: |
695 for j in range(i + 1, len(IPServer_port_numbers)): |
743 if (port1 == port2) and ( |
696 if IPServer_port_numbers[i][1] == IPServer_port_numbers[j][1]: |
744 (addr1 == addr2) # on the same network interface |
697 error_message = _("Error: Modbus/IP Servers %{a1}.x and %{a2}.x use the same port number {a3}.\n").format( |
745 or (addr1 == "") or (addr1 == "*") or (addr1 == "#ANY#") # or one (or both) of the servers |
698 a1=_lt_to_str(IPServer_port_numbers[i][0]), |
746 or (addr2 == "") or (addr2 == "*") or (addr2 == "#ANY#") # use all available network interfaces |
699 a2=_lt_to_str(IPServer_port_numbers[j][0]), |
747 ): |
700 a3=IPServer_port_numbers[j][1]) |
748 error_message = _("Error: Modbus plugin nodes %{a1}.x and %{a2}.x use same port number \"{a3}\" " + |
|
749 "on the same (or overlapping) network interfaces \"{a4}\" and \"{a5}\".\n").format( |
|
750 a1=_lt_to_str(loc1), a2=_lt_to_str(loc2), a3=port1, a4=addr1, a5=addr2) |
701 self.FatalError(error_message) |
751 self.FatalError(error_message) |
702 #self.GetCTRoot().logger.write_warning(error_message) |
|
703 #raise Exception |
|
704 |
752 |
705 # Determine the current location in Beremiz's project configuration |
753 # Determine the current location in Beremiz's project configuration |
706 # tree |
754 # tree |
707 current_location = self.GetCurrentLocation() |
755 current_location = self.GetCurrentLocation() |
708 |
756 |