ottowayi / pycomm3

A Python Ethernet/IP library for communicating with Allen-Bradley PLCs.
MIT License
397 stars 88 forks source link

Generic CIP Read #269

Closed AndrewStuhr closed 1 year ago

AndrewStuhr commented 1 year ago

Hello again CIP wizard!

I'm attempting to read barcode data from a Cognex camera (DM374X) using the CIP Driver. The camera has Ethernet/IP enabled, and I've read it's MAC address using this library.

However, I'm stuck performing a simple read:

with CIPDriver('192.168.100.171') as drive:
    param = drive.generic_message(
        service=Services.get_attribute_single,
        class_code=b'\x79',
        instance=11,  # Instance 11 has the result data in the protocol document
        attribute=b'\x10',
        data_type=INT,
        connected=False,
        unconnected_send=True,
        route_path=True,
        name='pf525_param'
    )
    print(param)

I'm confused because some objects in the documentation have a class & attribute, yet others show just an instance number. Yet I have to provide all 3 to perform a read with drive.generic_message().

Reader Object has class x79 and various attributes: image

Input Assembly Object has instance ID 11, but no apparent class or attribute #: image

This is my first attempt to use the CIP driver directly, so if it seems like I'm making an obvious mistake, that's probably the case!

Thanks again for all your development on this library : )

AndrewStuhr commented 1 year ago

On a separate note, I'm running the following code to get the attributes from the reader object (0x79):

with CIPDriver('192.168.100.171') as drive:
    param = drive.generic_message(
        service=Services.get_attribute_list ,
        class_code=b'\x79',
        instance=1,
        # attribute=b'\x10',
        # data_type=INT,
        connected=False,
        unconnected_send=True,
        route_path=True,
        name='pf525_param'
    )
    print(param.value)

The results are:

b'\n\x05\x06\x00\x03\x02 y$\xc8\x00\x01'

I was expecting to see 0x9, 0xA, 0xB, etc. to match the list in the first picture above. Any idea what I'm doing wrong?

ottowayi commented 1 year ago

I think this is a case of generic_message being a bit confusing. I would try without unconnected_send=True, that is only needed if connected=False and you have a route path. route_path=True means just use the route the driver was configured with, in this case it's just an IP address with no additional route that would require an unconnected send to forward the request to the final destination.

For the first example, you're trying to read attribute 16 (0x10), does this object have that attribute? And I'm not sure if the instance number is correct, it looks like instance 11 only refers to the assembly object. Maybe also try using instance 1?

For the assembly read, try with the assembly class code (0x04) and attribute 3 (the data attribute), instance 11 looks correct.

For the get attribute list, I'm not sure.. but try again without the unconnected send and see what that returns.

And a minor comment, the name field is just an arbitrary identifier for the request to make it easier to identify in the logs. So you can leave that out if you want.

AndrewStuhr commented 1 year ago

Ottowayi... once again you prove you are THE man.

Removing unconnected_send=True fixed the issue. Now it's reading data!

Must I manually parse the results? I'm receiving this big chunk of data (below) which makes sense, but is there a function to break up the data into it's sections (I'm unsure on the technical term for sections here, I mean the "name" column in the picture below).

Results from reading attribute 0x10: data

Documentation of addressing: format

ottowayi commented 1 year ago

Great! Glad it worked. You don't have to decode it manually, you can create a Struct and use that for the data_type argument. Check the docs, they should show how to do it or I think in the examples. I'm on mobile right now so I can't test it, but it looks like a fairly simple structure