pjkundert / cpppo

Communications Protocol Python Parser and Originator -- EtherNet/IP CIP
https://hardconsulting.com/products/6-cpppo-enip-api
Other
334 stars 109 forks source link

Please help understanding cpppo library #12

Closed Jonathan-Developer closed 7 years ago

Jonathan-Developer commented 8 years ago

Hi Perry, i've started a project about Ethernet/IP and i've been reading some information about it.

I need to write a python program using this cpppo library that needs to connect to a server running an Ethernet/IP simulator by Molex in an specific IP address on port 44818.

Using an Ethernet/IP object reader I need to validate that I'm connected and I need to show data from the controller, in this case (RSLinx Server), a first step will be to read and show All Atributtes of the object 0x01 that corresponds to the identity. (there's an image attached in this post that shows the Molex Ethernet/IP Simulator)

I saw that the following code could be helpful for what I need:

from cpppo.server.enip import client from cpppo.server.enip.getattr import attribute_operations

HOST = "181.49.9.154" #this is the IP address running the Ethernet/IP Sim device TAGS = ["@4/100/3"] # I don't know what this line means, is it like an identity address?

with client.connector(host=HOST) as conn: for index, descr, op, reply, status, value in conn.synchronous( operations=attribute_operations( TAGS, route_path=[], send_path='' )): print(": %20s: %s" % (descr, value))

_the output of the code is this: : Single G_AS @0x0004/100/3: None

is this output (: Single G_A_S) an identity or something in the simulator by Molex running in 181.49.9.154? @0x0004/100/3: None. I see that I didn't catch anything in @0x0004/100/3 (don't know what it means btw)

Browsing the Internet I saw this code that might be helpful on getting the 0x01 object identity:

request = cpppo.dotdict({'service': 0x01, 'path':{'segment':[{'class':Ix.class_id},{'instance':Ix.instance_id}]}}) gaa = Ix.request( request ) log.normal( "Identity Get Attributes All: %r, data: %s", gaa, enip.enip_format( request ))

but adding this code above to the first set of coding I get an error: request = cpppo.dotdict({'service': 0x01, 'path':{'segment':[{'class':Ix.class_id},{'instance':Ix.instance_id}]}}) NameError: name 'cpppo' is not defined

I don't know why I'm getting this error with those lines because there is the cpppo.server.enip import

Thanks for taking your time to read this post.

any help would be really appreciate it!

Thanks again Perry ethernet_ip server molex

pjkundert commented 8 years ago

I would recommend looking at the documentation in https://github.com/pjkundert/cpppo.git

You can obtain the CIP Identity information using the List Identity command:

python -m cpppo.server.enip.client --list-identity -a localhost

or:

python -m cpppo.server.enip.list_identity_simple localhost

Look in cpppo/server/enip/list_identity_simple.py for details of how to use the cpppo.server.enip.client API to issue the List Identity command.

However, you are wanting to read the Identity Class object, Instance 1 Attributes instead, to obtain the Identity information.

Since you must use Get Attribute Single or Get Attributes All to do this, and since these only deal in SINT or USINT single-byte data, I would recommend using the cpppo.server.enip.get_attribute proxy or proxy_simple API to do this. These can read the raw data, and convert it into the correct CIP data type for you:

>>> from cpppo.server.enip.get_attribute import proxy_simple
>>> list( proxy_simple("localhost").read([ ('@1/1/7', 'SSTRING') ]) )
[[u'PowerFlex/20-COMM-E']]

Of course, replace "localhost" with the IP address of your device.

This is all detailed in the README.org (and README.pdf) files included with Git source code, or available online at https://github.com/pjkundert/cpppo.git

Cheers,

Jonathan-Developer commented 8 years ago

Thank you very much for your help, I'll be taking a look at the readme.

Jonathan-Developer commented 8 years ago

Hi Perry, I've tried this code below but received None as an output for both params INT and SSTRING. I want to read all the attributes from the object 0x01 such as Vendors ID, Device Type, Product code, serial number and product name.

params = [('@1/1/1','INT'),('@1/1/7','SSTRING')]

How do I pass 0x01 as a parameter in the variable params, is this correct?: params = [('@0x01/1/1','INT'),('@0x01/1/7','SSTRING')]

I get the same output.....

WARNING:root:('@1/1/7', 'SSTRING') == None WARNING:root:('@1/1/1', 'INT') == None WARNING:root:('@1/1/7', 'SSTRING') == None WARNING:root:('@1/1/1', 'INT') == None

and so on .....

This is the Code:

import logging import sys import time import threading

from cpppo.server.enip import poll from cpppo.server.enip.get_attribute import proxy_simple as device params = [('@1/1/1','INT'),('@1/1/7','SSTRING')]

hostname = '181.49.9.154' #IP where the Simulator is. values = {} # { : , ... } poller = threading.Thread( target=poll.poll, args=(device,), kwargs={ 'address': (hostname, 44818), 'cycle': 1.0, 'timeout': 0.5, 'process': lambda par,val: values.update( { par: val } ), 'params': params, }) poller.daemon = True poller.start()

while True: while values: logging.warning( "%16s == %r", *values.popitem() ) time.sleep( .1 )

I forgot to mention that I've tried the command line you said to me and it works but I'm not getting the Vendors ID, Device Type, Product code, serial number and product name.

jonathan@debian:~/IBISA$ python -m cpppo.server.enip.client --list-identity -a 181.49.9.154 List Identity 0 from ('181.49.9.154', 44818): { "peer": [ "181.49.9.154", 44818 ], "enip.status": 1, "enip.sender_context.input": "array('c', '\x00\x00\x00\x00\x00\x00\x00\x00')", "enip.session_handle": 433, "enip.length": 0, "enip.command": 99, "enip.options": 0 }

Thanks so much Perry for the help you've been giving me.

pjkundert commented 8 years ago

It looks like it doesn't respond to the List Identity request over TCP/IP at least. Try the --udp option to force it to use UDP/IP instead. The List Identity request is pretty basic, so it should be supported by pretty much every EtherNet/IP CIP device.

By the way: use 3 back-ticks to surround code samples, to retain their formatting:

indent code
Jonathan-Developer commented 8 years ago

Hi Perry thanks for the reply, the problem was that the device simulator doesn't work with getting single attributes (get_attribute_single) it only works with get_attribute_all, instead of using TAGS = ["@1/1/1"] I used TAGS = ["@1/1"] and that way I received the output [77, 0, 11, 0, 1, 0, 1, 1, 0, 0, 142, 95, 48, 46, 13, 82, 83, 76, 105, 110, 120, 32, 83, 101, 114, 118, 101, 114], just what I wanted :)

Now I have to use a list to have all the attributes in there and then use the .join( chr( x ) for x in ) like this >>> ''.join( chr( x ) for x in [77, 0, 11, 0, 1, 0, 1, 1, 0, 0, 142, 95, 48, 46, 13, 82, 83, 76, 105, 110, 120, 32, 83, 101, 114, 118, 101, 114]) 'M\x00\x0b\x00\x01\x00\x01\x01\x00\x00\x8e_0.\rRSLinx Server'

still learning python, I'm getting there :)

Thanks again

pjkundert commented 8 years ago

I recommend checking out the cpppo.server.enip.get_attribute proxy API. It can request all attributes and parse all of the expected attributes into their respective CIP types. See the "Identity" parameter definition...

Jonathan-Developer commented 8 years ago

Hi Perry, I want to get All attributes in instance 1 using .read or .read_details, I had read the whole get_attribute python file and saw the read and read_details functions.

The Simulated server I'm working with doesn't work with getting singles attributes, I can only get them with get attribute All TAGS = ["@1/1"] which is the instance.

I've tested the following code but I got a None when executing. I've tried in the* function getVia()* passing (@class,instance,attribute) just to see if the read function works getting single attributes, but No, I got None as output for vendor and product_name. In function read_1() I pass as a parameter only @class,instance and then I print value just to see what type of object I'm getting but I got None as an output aswell.

from cpppo.server.enip import client
from cpppo.server.enip.getattr import attribute_operations
from cpppo.server.enip.getattr import proxy
import contextlib

HOST = "181.49.9.154"
TAGS = ["@1/1"]

def getVia():
    via = proxy( HOST )
    with via:
        vendor,product_name = via.read( [('@1/1/1','INT'), ('@1/1/7','SSTRING')] )
        print '1) in getVia(), vendor= ' + str(vendor) + ' , product_name= ' + str(product_name)

def read_1():
    via = proxy( HOST )
    with contextlib.closing( via.read( [ ("@1/1") ] )) as reader: # test if I get all attributes from instance 1
        value    = next( reader )
        print '2) in read_1(), value= ' + str(value)

def main():
    getVia()
    read_1()

main()

Should I need to use read_details? I've tried using a code in the get_attribute which uses the details function with no luck.

Thanks in advance for any help

pjkundert commented 8 years ago

I'd recommend increasing the logging level, so you can see the details of exactly what kind of error you are getting back from the simulator. If it only implements Get Attributes All, there might be other things it doesn't do.

import logging

logging.getLogger().setLevel( logging.INFO )

Try using proxy_simple instead of proxy. There's a good chance that it doesn't do the "Routing" style of Unconnected requests (eg. like a MicroLogix, or other simple EtherNet/IP CIP devices).

-pjk

-pjk

On Mon, Mar 7, 2016 at 3:13 PM, jonwolf-ibisa notifications@github.com wrote:

Hi Perry, _I want to get All attributes in instance 1 using .read or .readdetails, I had read the whole get_attribute python file and saw the _read and readdetails functions.

The Simulated server I'm working with doesn't work with getting singles attributes, I can only get them with get attribute All TAGS = ["@1/1"] which is the instance.

I've tested the following code but I got a None when executing. I've tried in the* function getVia()* passing (@class https://github.com/class,instance,attribute) just to see if the read function works getting single attributes, but No, I got None as output for vendor and product_name. In _function read1() I pass as a parameter only @class https://github.com/class,instance and then I print value just to see what type of object I'm getting but I got None as an output aswell.

from cpppo.server.enip import client from cpppo.server.enip.getattr import attribute_operations from cpppo.server.enip.getattr import proxy import contextlib

HOST = "181.49.9.154" TAGS = ["@1/1"]

def getVia(): via = proxy( HOST ) with via: vendor,product_name = via.read( [('@1/1/1','INT'), ('@1/1/7','SSTRING')] ) print '1) in getVia(), vendor= ' + str(vendor) + ' , product_name= ' + str(product_name)

def read_1(): via = proxy( HOST ) with contextlib.closing( via.read( [ ("@1/1") ] )) as reader: # test if I get all attributes from instance 1 value = next( reader ) print '2) in read_1(), value= ' + str(value)

def main(): getVia() read_1()

main()

Should I need to use read_details? I've tried using a code in the get_attribute which uses the details function with no luck.

Thanks in advance for any help

— Reply to this email directly or view it on GitHub https://github.com/pjkundert/cpppo/issues/12#issuecomment-193479372.

Jonathan-Developer commented 8 years ago

Hi Perry, I've added the import and line about logging and change proxy to proxy_single and got this for output:

No handlers could be found for logger "enip.cli" 1) in getVia, vendor= None , product_name= None 2) in read_1, value= None On Mar 7, 2016 6:42 PM, "Perry Kundert" notifications@github.com wrote:

I'd recommend increasing the logging level, so you can see the details of exactly what kind of error you are getting back from the simulator. If it only implements Get Attributes All, there might be other things it doesn't do.

import logging

logging.getLogger().setLevel( logging.INFO )

Try using proxy_simple instead of proxy. There's a good chance that it doesn't do the "Routing" style of Unconnected requests (eg. like a MicroLogix, or other simple EtherNet/IP CIP devices).

-pjk

-pjk

On Mon, Mar 7, 2016 at 3:13 PM, jonwolf-ibisa notifications@github.com wrote:

Hi Perry, _I want to get All attributes in instance 1 using .read or .readdetails, I had read the whole get_attribute python file and saw the _read and readdetails functions.

The Simulated server I'm working with doesn't work with getting singles attributes, I can only get them with get attribute All TAGS = ["@1/1"] which is the instance.

I've tested the following code but I got a None when executing. I've tried in the* function getVia()* passing (@class <https://github.com/class ,instance,attribute) just to see if the read function works getting single attributes, but No, I got None as output for vendor and product_name. In function read_1() I pass as a parameter only @class https://github.com/class,instance and then I print value just to see what type of object I'm getting but I got None as an output aswell.

from cpppo.server.enip import client from cpppo.server.enip.getattr import attribute_operations from cpppo.server.enip.getattr import proxy import contextlib

HOST = "181.49.9.154" TAGS = ["@1/1"]

def getVia(): via = proxy( HOST ) with via: vendor,product_name = via.read( [('@1/1/1','INT'), ('@1/1/7','SSTRING')] ) print '1) in getVia(), vendor= ' + str(vendor) + ' , product_name= ' + str(product_name)

def read_1(): via = proxy( HOST ) with contextlib.closing( via.read( [ ("@1/1") ] )) as reader: # test if I get all attributes from instance 1 value = next( reader ) print '2) in read_1(), value= ' + str(value)

def main(): getVia() read_1()

main()

Should I need to use read_details? I've tried using a code in the get_attribute which uses the details function with no luck.

Thanks in advance for any help

— Reply to this email directly or view it on GitHub https://github.com/pjkundert/cpppo/issues/12#issuecomment-193479372.

— Reply to this email directly or view it on GitHub https://github.com/pjkundert/cpppo/issues/12#issuecomment-193497809.

pjkundert commented 8 years ago

OK, lets set up logging properly first:

import logging
import cpppo

cpppo.log_cfg['level'] = logging.INFO
logging.basicConfig( **cpppo.log_cfg )

-pjk

On Mon, Mar 7, 2016 at 4:38 PM, jonwolf-ibisa notifications@github.com wrote:

Hi Perry, I've added the import and line about logging and change proxy to proxy_single and got this for output:

No handlers could be found for logger "enip.cli" 1) in getVia, vendor= None , product_name= None 2) in read_1, value= None

On Mar 7, 2016 6:42 PM, "Perry Kundert" notifications@github.com wrote:

I'd recommend increasing the logging level, so you can see the details of exactly what kind of error you are getting back from the simulator. If it only implements Get Attributes All, there might be other things it doesn't do.

import logging

logging.getLogger().setLevel( logging.INFO )

Try using proxy_simple instead of proxy. There's a good chance that it doesn't do the "Routing" style of Unconnected requests (eg. like a MicroLogix, or other simple EtherNet/IP CIP devices).

-pjk

-pjk

On Mon, Mar 7, 2016 at 3:13 PM, jonwolf-ibisa notifications@github.com wrote:

Hi Perry, _I want to get All attributes in instance 1 using .read or .readdetails, I had read the whole get_attribute python file and saw the _read and readdetails functions.

The Simulated server I'm working with doesn't work with getting singles attributes, I can only get them with get attribute All TAGS = ["@1/1"] which is the instance.

I've tested the following code but I got a None when executing. I've tried in the* function getVia()* passing (@class <https://github.com/class ,instance,attribute) just to see if the read function works getting single attributes, but No, I got None as output for vendor and product_name. In function read_1() I pass as a parameter only @class https://github.com/class,instance and then I print value just to see what type of object I'm getting but I got None as an output aswell.

from cpppo.server.enip import client from cpppo.server.enip.getattr import attribute_operations from cpppo.server.enip.getattr import proxy import contextlib

HOST = "181.49.9.154" TAGS = ["@1/1"]

def getVia(): via = proxy( HOST ) with via: vendor,product_name = via.read( [('@1/1/1','INT'), ('@1/1/7','SSTRING')] ) print '1) in getVia(), vendor= ' + str(vendor) + ' , product_name= ' + str(product_name)

def read_1(): via = proxy( HOST ) with contextlib.closing( via.read( [ ("@1/1") ] )) as reader: # test if I get all attributes from instance 1 value = next( reader ) print '2) in read_1(), value= ' + str(value)

def main(): getVia() read_1()

main()

Should I need to use read_details? I've tried using a code in the get_attribute which uses the details function with no luck.

Thanks in advance for any help

— Reply to this email directly or view it on GitHub https://github.com/pjkundert/cpppo/issues/12#issuecomment-193479372.

— Reply to this email directly or view it on GitHub https://github.com/pjkundert/cpppo/issues/12#issuecomment-193497809.

— Reply to this email directly or view it on GitHub https://github.com/pjkundert/cpppo/issues/12#issuecomment-193508444.