Open thalesmaoa opened 1 year ago
I figure out how to send a generic message, but I need to open a socket to exchange information over port 2222. I'm having a lot of trouble of doing that.
plc = CIPDriver(host)
plc.open()
response = plc.generic_message(
service=0x54,
class_code=ClassCode.connection_manager,
instance=b"\x01",
#attribute=b"\x01",
request_data=b"\x03\xfa\x00\x00\x00\x00\xc1\x61\x83\x6b\x01\x00\x05\x05\x98\xb5\x40\xeb\x01\x00\x00\x00\x10\x27\x00\x00\x10\xc8\x10\x27\x00\x00\x56\xc8\x21\x09\x34\x04\x80\x02\x0c\x00\xd7\xdc\x02\x00\x20\x04\x24\x80\x2c\x70\x2c\x64",
data_type=REAL,
connected=False,
unconnected_send=False,
route_path=False,
)
Changing the port is easy, use the <ipaddress>:<port>
syntax for you host
variable. Changing the socket type will require you to subclass the driver and override the socket creation. But, you may not have to do any of that. What you're seeing in the pcaps is probably I/O traffic, do they provide any examples on how to use a MSG instruction in Logix? If so, pycomm3 should work as is. Can you post the EDS?
Hi @ottowayi , I really appreciate your reply. You are probably right, but I didn't understand it correct. I've attached the eds and the pcap from the PLC (can open using Wireshark). I can see that, after successful reply, it starts to send CIP IO messages.
When I try the same using pycomm3. It acknowledge, but then, it drops.
Expected behavior:
1 0.000000 10.254.1.253 10.254.1.33 TCP 74 36183 → 44818 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM TSval=1295132108 TSecr=0 WS=128
2 0.000369 10.254.1.33 10.254.1.253 TCP 60 44818 → 36183 [SYN, ACK] Seq=0 Ack=1 Win=2048 Len=0 MSS=1460
3 0.000430 10.254.1.253 10.254.1.33 TCP 54 36183 → 44818 [ACK] Seq=1 Ack=1 Win=64240 Len=0
4 0.019890 10.254.1.253 10.254.1.33 ENIP 82 Register Session (Req), Session: 0x00000000
5 0.020381 10.254.1.33 10.254.1.253 ENIP 82 Register Session (Rsp), Session: 0x05010000
6 0.020426 10.254.1.253 10.254.1.33 TCP 54 36183 → 44818 [ACK] Seq=29 Ack=29 Win=64212 Len=0
7 0.049822 10.254.1.253 10.254.1.33 CIP CM 154 Connection Manager - Forward Open (Assembly)
8 0.050886 10.254.1.33 10.254.1.253 CIP CM 164 Success: Connection Manager - Forward Open (Assembly)
9 0.051623 10.254.1.33 10.254.1.253 CIP I/O 146 Connection: ID=0x6B8361C1, SEQ=0000000000, T->O
10 0.054190 10.254.1.33 10.254.1.253 CIP I/O 146 Connection: ID=0x6B8361C1, SEQ=0000000001, T->O
What I get using pycomm3:
0.049822 10.254.1.253 10.254.1.33 CIP CM 154 Connection Manager - Forward Open (Assembly)
0.050886 10.254.1.33 10.254.1.253 CIP CM 164 Success: Connection Manager - Forward Open (Assembly)
And then it drops due to timeout.
Not sure if it is a socket problem, but I need something to establish. One example is https://github.com/EIPStackGroup/OpENer
Oh I see what's happening, as soon as you register a session the device creates an I/O (class 1) connection and starts sending data (that's the traffic you see on port 2222). Unfortunately pycomm3 is only designed for message/class 3 connections. I'm guessing the forward open data you're sending is telling it you want to open an I/O connection.
What data are you expecting to get from this device? If you want to read I/O data, I think it will still work using pycomm3. There isn't any built in functionality for it, like there is for reading tags, but using a generic message should be possible.
Can you try doing:
from pycomm3 import ClassCode, Services, ModuleIdentityObject, CIPDriver
with CIPDriver(host) as plc:
response = plc.generic_message(
class_code=ClassCode.identity_object,
instance=b"\x01",
service=Services.get_attributes_all,
data_type=ModuleIdentityObject,
connected=False, # try with True as well
#unconnected_send=False, # set to True when connected is False and host has a route
# if host is just an IP, the omit all together
name="identity",
)
print(response)
Thanks for point it out. I'm still learning from ODVA doc, and yes! I can request a class 1 connection, but I need to open a socket using udp to receive the traffic. Since I can't do that, it just drop due to timeout.
The device gives me Analog Inputs and Outputs. Also it has some Digital Input/Output. I just want to integrate it to NodeRed. It won't be a problem to get an array since I now how to map the Assembly info, but I'm kind of lost here.
I tried as you suggested and I can identify the object:
identity, {'product_code': 56535, 'product_name': 'Cube67+ BN-E V2', 'product_type': 'Communications Adapter', 'revision': {'major': 2, 'minor': 6}, ...}, ModuleIdentityObject(UINT(name='vendor'), UINT(name='product_type'), UINT(name='product_code'), Revision(name='revision'), BYTES(name='status'), UDINT(name='serial'), SHORT_STRING(name='product_name')), None
I tried the assembly:
with CIPDriver(host) as plc:
response = plc.generic_message(
class_code=ClassCode.assembly,
instance=b"\x01",
service=Services.get_attributes_all,
data_type=ModuleIdentityObject,
connected=False, # try with True as well
#unconnected_send=False, # set to True when connected is False and host has a route
# if host is just an IP, the omit all together
name="identity",
)
print(response)
identity, None, ModuleIdentityObject(UINT(name='vendor'), UINT(name='product_type'), UINT(name='product_code'), Revision(name='revision'), BYTES(name='status'), UDINT(name='serial'), SHORT_STRING(name='product_name')), Service not supported
Probably because I'm using ModuleIdentifyObject.
What makes me more confused is that I must configure T->O and O->T. I can't see how is it possible with pycomm3 without request_data
hex
.
I'm probably missing something.
Do you see the possibility to request data using generic message?
Apologies for the late reply, but I think the error response is actually good news. It's saying the get attributes all service isn't supported, but it's a response and that means the device got the request. I'm not too familiar with the assembly object and how it works, but you could try using the get_attribute_single
service, keep instance
at 1, but set attribute
to 3, and remove the data_type
or set it to None
. If this works, you'll get the data
attribute back for instance 1 of the assembly. Without specifying the data type you should get back a bytes
object and see how the data structured. If you can determine that you can create a type to use as data_type
and have it decode it for you. Or leaving it as None
you can handle the decoding yourself.
It took some time to recover the module to keep testing. I really appreciate your help.
The device response well to pycomm3. From my first message, I can request the CIP IO. The problem is that I can't open UDP socket due to lack of knowledge.
I tried as you suggested but I'm getting an error:
with CIPDriver(host) as plc:
response = plc.generic_message(
class_code=ClassCode.assembly,
instance=b"\x01",
service=Services.get_attribute_single,
data_type=None,
connected=False, # try with True as well
attribute=b"\x04",
#unconnected_send=False, # set to True when connected is False and host has a route
# if host is just an IP, the omit all together
name="identity",
)
print(response)
identity, b'', None, Destination unknown, class unsupported, instance undefined or structure element undefined (see extended status) - Extended status out of memory (05, 00)
To be honest, I had the feeling that I don't know what I am probing, and what I should expect.
I did exactly this: https://github.com/ASolchen/pico-eip @ottowayi is correct, it establishes a session via TCP port 44818 and does io (implicit) data back and forth on UDP port 2222. My code is buggy and I don't fully understand all of what is going on, but it does work. I used the example at https://github.com/EIPStackGroup/OpENer to capture packets and replicated it in python. It does the EIP commands "List Services", "Register Session", and "Send RR data". After that the "Scanner" (PLC) and "Adapter" (IO Device) send unsolicited to each other at the agreed upon RPI rate on UDP port 2222. Feel free to let me know if I missed anything or can explain more about how this works.
As a follow-up to my last comment, I would love to see this functionality added to pycomm3. The repo I have is actually the other side of the transaction. My code replicates a device ("Adapter" in CIP docs) that a PLC ("Scanner") talks to. If pycomm3 had the connected IO capability it would be the Scanner talking to a device. At least that's how I see the library; as a "Client." Pycomm3 treats the PLC as the server since it opens a port and listens. The flow of info is initiated by Pycomm3. With connected I/O the "Scanner" is the "Client," initiating communications to the "Server" (Adapter). So I see much less effort needed to add the ability to be a scanner, since functions to initiate it are already here using Generic CIP commands, e.g. List Servers 0x0004, or Register Session 0x0065. All that needs to be added is UDP sockets to actually send and receive I/O data. From my testing it, seems to work better to spawn a new thread to handle the I/O data, but this may just because I can't seem to figure out how to handle socket timeouts.
I looked a bit at getting pycomm3 to be a scanner. The capture below shows an actual PLC to OpenEner scanner.
O->T Network Connection Parameters: 0x4826
0... .... .... .... = Redundant Owner: Non-Redundant (0)
.10. .... .... .... = Connection Type: Point to Point (2)
.... 10.. .... .... = Priority: Scheduled (2)
.... ..0. .... .... = Connection Size Type: Fixed (0)
.... ...0 0010 0110 = Connection Size: 38 bytes
T->O RPI: 30.000ms
T->O Network Connection Parameters: 0x4822
0... .... .... .... = Redundant Owner: Non-Redundant (0)
.10. .... .... .... = Connection Type: Point to Point (2)
.... 10.. .... .... = Priority: Scheduled (2)
.... ..0. .... .... = Connection Size Type: Fixed (0)
.... ...0 0010 0010 = Connection Size: 34 bytes
Transport Type/Trigger: 0x81, Direction: Server, Trigger: Cyclic, Class: 1
Connection Path Size: 15 words
This is in the net_params of the _fowardopen method. Unfortunately, these seem to be hard-coded in the method:
init_net_params = 0b_0100_0010_0000_0000 # CIP Vol 1 - 3-5.5.1.1
This value would set the following:
Non-redundant, Point to Point, Low Priority, Fixed Size
If we could change this to Scheduled priority we may be able to get it connected. From the captures I have, it only needs this forwardopen command formatted correctly, and the 2 devices start the UDP packets. We would just need to start sending and receiving datagrams.
Hi, I'm starting with a Ethernet/IP device which I have a EDS file. After some sniffing using Wireshark, I've noticed that it uses explicit message using UDP.
After a lot of reading, I wasn't able to understand if it is possible to establish this type of connection. Are there any example on how to perform it?