I am working to build a Python script to communicate with an EtherNet/IP device (Graco PD2K spray system). The only documentation provided by the vendor is how to configure an Allen Bradley PLC as the client to communicate with the device.
Using the following code, I can read the array of attributes at Assembly Instance 100:
from cpppo.server.enip.get_attribute import proxy_simple
via = proxy_simple('192.168.10.5')
with via:
data, = via.read( [('@4/100/3','DINT')] )
... which results in receiving back the expected array:
[0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(39 x 32-bit integers)
When attempting to write to the attributes at Assembly instance 150, I receive True
back from the controller, but the controller does not update the parameters. It is expecting 25 x 32-bit integer array:
with via:
result, = via.read([('@4/150/3=(DINT)4, 1, 0, 0, 1, 0, 0, 0, 1, 0, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0','@4/150/3')],1)
The output from above is:
@4/150/3=(DINT)4, 1, 0, 0, 1, 0, 0, 0, 1, 0, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 == True
If I add one integer to the array (or subtract, or attempt to set other than @4/150/3, I get back None
, so it is clear I am close on the format and the command is getting through.
I have reached out to the vendor multiple times, and they insist it is a problem with Python (or, more specifically, they do not support Python and recommend integrating with a PLC).
I am wondering if the "Configuration" parameter at Assembly Instance 1 is the issue (see image above). I have tried multiple versions of the following code to try to write that parameter. Not fully understanding the EtherNet/IP protocol, I'm not even sure what that particular instance does -- however, that it is a parameter in an Allen-Bradley config indicates it is important in this case.
Code attempted:
result, = via.read([('@4/1/3=(USINT)0','@4/1/3')],1)
I have tried using the Molex EnIP utility, as well as something similar on SourceForge to take Python out of the equation, but results are similar. I have also tried the PyComm3 module, but I can't even get it to return Id information. I have also tried using -vvv
with the CPPPO command line utils:
python -m cpppo.server.enip.get_attribute -a 192.168.10.5 '@4/150/3=(DINT)4, 1, 0, 0, 1, 0, 0, 0, 1, 0, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0' -vv -S
Results in (along with much more output that I don't believe is relevant):
11-09 12:11:18.119 MainThread enip.cli DETAIL issue Sending 1 (Context b'0')
11-09 12:11:18.120 MainThread enip.cli DETAIL pipeline Issuing 0/ 1; curr: 0 - last: -1 == 1 depth vs. max 0
11-09 12:11:18.124 MainThread enip.cli DETAIL __next__ Client CIP Rcvd: {
"send_data.interface": 0,
"send_data.timeout": 8,
"send_data.CPF.count": 2,
"send_data.CPF.item[0].type_id": 0,
"send_data.CPF.item[0].length": 0,
"send_data.CPF.item[1].type_id": 178,
"send_data.CPF.item[1].length": 4,
"send_data.CPF.item[1].unconnected_send.request.input": "array('B', [144, 0, 0, 0])",
"send_data.CPF.item[1].unconnected_send.request.service": 144,
"send_data.CPF.item[1].unconnected_send.request.status": 0,
"send_data.CPF.item[1].unconnected_send.request.status_ext.size": 0,
"send_data.CPF.item[1].unconnected_send.request.set_attribute_single": true
}
11-09 12:11:18.124 MainThread enip.cli DETAIL collect Receive 1 (Context b'0')
11-09 12:11:18.124 MainThread enip.cli DETAIL pipeline Completed 1/ 1; curr: 0 - last: 0 == 0 depth vs. max 0
Mon Nov 9 12:11:18 2020: 0: Single S_A_S @0x0004/150/3 == True
11-09 12:11:18.124 MainThread enip.cli DETAIL pipeline Pipelined 1/ 1; curr: 0 - last: 0 == 0 depth vs. max 0
11-09 12:11:18.125 MainThread enip.get NORMAL main 1 requests in 0.006s at pipeline depth 0; 153.919 TPS
Again, result of the request is True
, but the controller does not update any of the parameters.
I'm not sure what to try next...