Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please help understanding cpppo library #12

Closed
Jonathan-Developer opened this issue Feb 24, 2016 · 10 comments
Closed

Please help understanding cpppo library #12

Jonathan-Developer opened this issue Feb 24, 2016 · 10 comments

Comments

@Jonathan-Developer
Copy link

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_A_S @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
Copy link
Owner

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
Copy link
Author

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

@Jonathan-Developer
Copy link
Author

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
Copy link
Owner

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
Copy link
Author

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
Copy link
Owner

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
Copy link
Author

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
Copy link
Owner

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
.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 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
#12 (comment).

@Jonathan-Developer
Copy link
Author

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
.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 <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
#12 (comment).


Reply to this email directly or view it on GitHub
#12 (comment).

@pjkundert
Copy link
Owner

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
.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 <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
#12 (comment).


Reply to this email directly or view it on GitHub
#12 (comment).


Reply to this email directly or view it on GitHub
#12 (comment).

pjkundert added a commit that referenced this issue Jan 30, 2018
# This is the 1st commit message:

Initial foray in to support for generic CIP Service Code requests

# This is the commit message #2:

No requirement for existence of .multiple segment in failed responses

# This is the commit message #3:

Correct handling of service_code operations in client connector I/O

# This is the commit message #4:

HART Requests almost working
o Cannot derive HART from Logix; service codes overlap

# This is the commit message #5:

Initial working HART I/O card request

# This is the commit message #6:

Support intermixed Tags and already parsed operation in parse_operations

# This is the commit message #7:

Test and decode the Read primary variable response, however:
o Still broken; the CIP Encapsulation path is still suppsed to be to the
  Connection Manager @0x06/1!  The 0x52 Route Path is Port 1, Address 2,
  and the message path should be to @0x035D/8.

# This is the commit message #8:

Success.  Still needs cleanup

# This is the commit message #9:

Further attempts to refine HART pass-thru.
o HART I/O card is not responding as defined in documentation

# This is the commit message #10:

Cleanups for python3, source analysis, unit tests

# This is the commit message #11:

Attempt to parse Read Dynamic Variables reply; 3 unrecognzied bytes?

# This is the commit message #12:

Update to attempt to parse real HART I/O card response
o Minimal Read Dynamic Variables status response?  Not successful
o Implement minimal simulated pass-thru Init/Query, HART commands 1,2,3
o Minor changes to client.py Send RR Data, to have timeout and ticks
  compatible with RSLogix; no difference
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants