ruscito / pycomm

pycomm is a package that includes a collection of modules used to communicate with PLCs
MIT License
142 stars 85 forks source link

Compatibility with cpppo #34

Open johanfforsberg opened 7 years ago

johanfforsberg commented 7 years ago

Hi,

"cpppo" (https://github.com/pjkundert/cpppo) is a very cool python library that among other things can simulate a PLC locally. This is quite useful for testing without needing PLC hardware, and I've used it with other client libraries in the past with success. But somehow it does not play well with pycomm.

Here's the output of running a cpppo server and then connecting locally with pycomm from another process. The corresponding pycomm code is below.

Now I can't tell if this is an issue with pycomm or with cpppo, but to me it looks like something is missing in the client connection. Maybe I'm just not doing it right?

$ env/bin/python -m cpppo.server.enip -v a=DINT b=DINT
09-04 17:19:40.192     7f2d82de0700 enip.srv NORMAL   main       Delaying all responses by 0.0 seconds
09-04 17:19:40.192     7f2d82de0700 enip.srv NORMAL   main       Creating tag: a=DINT[1]
09-04 17:19:40.192     7f2d82de0700 enip.srv NORMAL   main       Creating tag: b=DINT[1]
09-04 17:19:40.192     7f2d82de0700 root     NORMAL   main       EtherNet/IP Simulator: ('', 44818)
09-04 17:19:40.193     7f2d82de0700 network  NORMAL   server_mai enip_srv server PID [21011] running on ('', 44818)
09-04 17:19:40.193     7f2d82de0700 network  NORMAL   server_mai enip_srv server PID [21011] responding to external done/disable signal
09-04 17:21:48.995     7f2d806b2700 enip.srv NORMAL   enip_srv   EtherNet/IP Server enip_54710 begins serving peer ('127.0.0.1', 54710)
09-04 17:21:49.005     7f2d806b2700 enip.lgx NORMAL   setup                         Logix.a             DINT[   1] == 0 Attribute   1 added
09-04 17:21:49.006     7f2d806b2700 enip.lgx NORMAL   setup                         Logix.b             DINT[   1] == 0 Attribute   2 added
09-04 17:21:49.026     7f2d806b2700 enip.dev ERROR    request    EtherNet/IP CIP error at 0 total bytes:
"N\x02 \x06$\x01\n\x05'\x04\t\x10\t\x10\x19q\x03\x00\x01\x00 \x02$\x01"
-^ (byte 0)

09-04 17:21:49.027     7f2d806b2700 enip.dev NORMAL   request    (0x9999,  1) UCMM Command 0x006f SendRRData failed with Exception: ( dfa_post.( select ) ) sub-machine terminated in a non-terminal state
Request: {
    "enip.status": 0, 
    "enip.sender_context.input": "array('c', '_pycomm_')", 
    "enip.session_handle": 2518093524, 
    "enip.length": 40, 
    "enip.CIP.send_data.interface": 0, 
    "enip.CIP.send_data.CPF.count": 2, 
    "enip.CIP.send_data.CPF.item[0].length": 0, 
    "enip.CIP.send_data.CPF.item[0].type_id": 0, 
    "enip.CIP.send_data.CPF.item[1].length": 24, 
    "enip.CIP.send_data.CPF.item[1].unconnected_send.request.input": "array('c', \"N\\x02 \\x06$\\x01\\n\\x05'\\x04\\t\\x10\\t\\x10\\x19q\\x03\\x00\\x01\\x00 \\x02$\\x01\")", 
    "enip.CIP.send_data.CPF.item[1].type_id": 178, 
    "enip.CIP.send_data.timeout": 10, 
    "enip.command": 111, 
    "enip.options": 0, 
    "addr": [
        "127.0.0.1", 
        54710
    ]
}
Traceback (most recent call last):
  File "/home/johfor/kits/cpppo/server/enip/device.py", line 990, in request
    CM.request( unc_send )
  File "/home/johfor/kits/cpppo/server/enip/device.py", line 1510, in request
    for i,(m,s) in enumerate( machine.run( path='request', source=source, data=data )):
  File "/home/johfor/kits/cpppo/automata.py", line 645, in run
    source=source, machine=machine, path=path, data=data, ending=ending ):
  File "/home/johfor/kits/cpppo/automata.py", line 1291, in delegate
    raise NonTerminal( "%s sub-machine terminated in a non-terminal state" % ( self ))
NonTerminal: ( dfa_post.( select ) ) sub-machine terminated in a non-terminal state

09-04 17:21:49.028     7f2d806b2700 enip.srv WARNING  enip_srv   Expected EtherNet/IP response encapsulated message; none found
09-04 17:21:49.028     7f2d806b2700 enip.srv WARNING  enip_srv   Session ended (server EtherNet/IP status: 0x08 == 8)
09-04 17:21:49.029     7f2d806b2700 enip.srv NORMAL   enip_srv   enip_54710 done; processed   2 requests over    92 bytes/   92 received (0 connections remain)

IPython:

In [13]: c = ClxDriver()
In [14]: c.open("127.0.0.1")
Out[14]: True

Doing something else at this point, like reading a tag, results in a "CommError: socket connection broken.".

shadetree01010100 commented 6 years ago

I ran into exactly this issue this week, and began working on a solution. Through much research I found that the cpppo server you (and I) are running is a generic CIP device, so it will not work with the AllenBradley drivers included with pycomm. From the examples and comments included I can only assume that it would work as advertised with those AB devices named, but I do not have one available to test with.

I am working on a fork to support CIP Generic Messaging, and have gotten it working with cpppo and an off-brand PLC with an EtherNet/IP interface. https://github.com/tyoungNIO/pycomm/blob/generic/pycomm/cip/cip_generic.py

As of today the generic branch still does not work with any sort of symbolic addressing, but if you supply a full path for the tags to your cpppo.server.enip, such as a@1/2/3=DINT, then a call to c.get_attribute_single(1, 2, 3) will return 4 bytes.

A PLC's IO data (including AB, I believe) will also be available from standard Class IDs, for example 10 for Analog Input. In my case, I can read the first four analog inputs from 10, 1, 3 through 10, 4, 3, each input is an instance of the AI class from 1 to 4, with the measured value stored in attribute 3. The second AI module begins at 10, 65, 3, and so forth, according to the manufacturer's documentation.

rajatkmehta commented 6 years ago

Hi, Can we get and set attribute for any CIP generic objects using Pycomm and CPPPO? Can Explicit and Implicit both supported by Pycomm and CPPPO? If one to select for the automation then which would you prefer?

Please reply, it's urgent.