diff --git a/.gitignore b/.gitignore index b7ea68c..97ace6d 100644 --- a/.gitignore +++ b/.gitignore @@ -110,3 +110,6 @@ pylogix/testing/ # PyCharm .idea/ + +# Testfile +testfile.py* \ No newline at end of file diff --git a/README.md b/README.md index 15b2e12..e38d562 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,14 @@ NOTE: If you are working with a Micro8xx PLC, you must set the Micro800 flag sin comm.Micro800 = True ``` +If you want to query module information for modules under a nested bus / backplane: +``` +# Uses custom CIP routing path +comm.GetModuleProperties(custom_routing_path=[(1, 0), (7, 2)]) +``` +The above code will fetch module information for slot 2 of a CompactLogix CPU CompactBus virtual backplane. + + ### Other Features Pylogix has features other than simply reading/writing. You can see all of them in the examples, I'll also list them here @@ -71,6 +79,7 @@ Pylogix has features other than simply reading/writing. You can see all of them * **Dustin Roeder** - *Maintainer* - [dmroeder](https://github.com/dmroeder) * **Fernando B.** - *Contributor* - [kodaman2](https://github.com/kodaman2) * **Ottowayi** - *Contributor* - [ottowayi](https://github.com/ottowayi) +* **Daniel Leicht** - *Contributor* - [daniel-leicht](https://github.com/daniel-leicht) ## License diff --git a/pylogix/eip.py b/pylogix/eip.py index 243d46b..52f68a4 100644 --- a/pylogix/eip.py +++ b/pylogix/eip.py @@ -161,11 +161,11 @@ def Discover(self): ''' return self._discover() - def GetModuleProperties(self, slot): + def GetModuleProperties(self, slot=None, custom_routing_path=None): ''' Get the properties of module in specified slot ''' - return self._getModuleProperties(slot) + return self._getModuleProperties(slot=slot, custom_routing_path=custom_routing_path) def Close(self): ''' @@ -625,12 +625,15 @@ def _discover(self): return Response(None, devices, 0) - def _getModuleProperties(self, slot): + def _getModuleProperties(self, slot, custom_routing_path=None): ''' Request the properties of a module in a particular - slot. Returns LgxDevice + slot or module that is located in specific routing path. Returns Response object. ''' if not self._connect(): return None + + if (slot == None) and (custom_routing_path == None): + raise ValueError("Either slot number or a routing path should be specified") AttributeService = 0x01 AttributeSize = 0x02 @@ -642,18 +645,39 @@ def _getModuleProperties(self, slot): Reserved = 0x00 Backplane = 0x01 LinkAddress = slot - - AttributePacket = pack('<10B', - AttributeService, - AttributeSize, - AttributeClassType, - AttributeClass, - AttributeInstanceType, - AttributeInstance, - PathRouteSize, - Reserved, - Backplane, - LinkAddress) + + AttributePacket = pack('<6B', + AttributeService, + AttributeSize, + AttributeClassType, + AttributeClass, + AttributeInstanceType, + AttributeInstance) + + if not custom_routing_path: + AttributePacket += pack('<4B', + PathRouteSize, + Reserved, + Backplane, + LinkAddress) + else: + """ + Use custom routing -> it should be a list with tuples containing pairs of port number & slot address. + """ + custom_route_path_size = len(custom_routing_path) + route_path = pack('<2B', + custom_route_path_size, + Reserved) + + for routing_pair in custom_routing_path: + assert len(routing_pair) == 2, "Routing path should be list of 2-tuples." + port_segment = 0b00001111 & routing_pair[0] + link_address = routing_pair[1] + route_path += pack("<2B", + port_segment, + link_address) + + AttributePacket += route_path frame = self._buildCIPUnconnectedSend() + AttributePacket eipHeader = self._buildEIPSendRRDataHeader(len(frame)) + frame