dmroeder / pylogix

Read/Write data from Allen Bradley Compact/Control Logix PLC's
Apache License 2.0
599 stars 182 forks source link

Reads using internal PLC Address #140

Closed AndrewStuhr closed 3 years ago

AndrewStuhr commented 4 years ago

Hi

I see that comm.getTagList() returns a tag's instance ID. For example:

{'TagName': 'Program:MainProgram.DAQ_Bool4', 'InstanceID': 6353, 'SymbolType': 193, 'DataTypeValue': 193, 'DataType': 'BOOL', 'Array': 0, 'Struct': 0, 'Size': 0, 'AccessRight': None, 'Internal': None, 'Meta': None, 'Scope0': None, 'Scope1': None, 'Bytes': None}

Is there a way to read tags using their instanceIDs instead of tag names to speed up reads?

The reason I'm asking is because of a situation that I recently encountered. I was using Kepware to log tags from a controlLogix, and the reads were too slow. It turns out I was using a setting that Kepware calls 'Symbolic Mode' that uses the full tag name for read requests. Kepware has another setting called 'Non-Logical Blocking Mode' that reads the internal PLC address during initialization and uses these for read requests. This allows packets to carry more data and speed up reads, which I can verify does occur!

I do not know if instanceID is what Kepware is using.

I know passing in a tag's data type is another way to speed up PyLogix reads, but thought I'd inquire if you know anything about this.

Thanks!

David-2D3FE9 commented 4 years ago

I don't how to deal with ID, but below the experience that I can share

I work with Pylogix since march to monitor the behavior of some PLC applications that i code. so I payed attention to the communication load i produce by this monitoring

Today I still have a PLC at home, so I took 20 minutes to rebuild the test I done in march: I read 1k DINT, from an array in PLC, by 3 methods, at begin and end of each test I record timestamps

  1. Reading 1k individual tags => tooks 40 seconds
  2. Reading tags 20 by 20 in tag list (executed 50 times to reach the 1k amount) => tooks av. 2,3 seconds
  3. By reading an array of 1k dimention => tooks 0,08 seconds

That show that, for the same amount of data exchanged, it will not be wasted time to prepare the outgoing data. If possible, in the PLC put all data you want to transfert in an array (array[1]:= tag1, array[2]:= tag2, ... ), that will reduce the number of separate requests, so bring you able to increase the amount of refresh in a given windows time

For an higher precision (eg: for external trending) you can record each value change with FIFO arrays in PLC, then with pylogix each one or two second get the full table (create a FIFO to recrod the value and a FIFO to record the PLC timestamp, and after geting the both arrays with pylogix, use some code to remove duplicate entries)

ottowayi commented 4 years ago

It appears to only use the symbolic addressing. Passing in the data type allows it to bypass the initial read to retrieve the data type, but that does not return the instance id. In order to get those you would need to read the tag list first, which is what I do. I suppose it would be possible for pylogix to cache those instance ids if a user would first get the tag list and use symbolic if not. I require the tag list upload in order to support the structure reads/writes and to track the response size to make sure it uses fragmented requests or makes a new multi request automatically.

kyle-github commented 4 years ago

I have not experimented yet with ID-based addressing. Since my library is in C, i do not have all the nice things that Python has that make pulling the tag definitions "easy." I do have mixed library/user code that does it and I suppose I could let the user side of the system take those IDs and use them directly.

That said, once I added request packing, the overall throughput went up so much that using slightly faster decoding on the PLC side does not add that much. Also key is that I negotiate very large packets with the PLC when possible. When you combine the 4k packets with request packing, you can read upwards of 300 tags in just a few milliseconds. Even using symbolic addressing.

However my model, very long lived tag handles, and pylogix model are quite different. YMMV.

As far as I have seen, the ID-based addressing only works at the very top level tag. I have not seen any examples that show any different.

ottowayi commented 4 years ago

As far as I have seen, the ID-based addressing only works at the very top level tag. I have not seen any examples that show any different.

Yeah that is correct, the instance ID is only used for the base tag. The rest of the attributes must be symbolic. And instances IDs are generated on download, so they can change.

I saw some increase in performance with adding the ID addressing, but the biggest was using instance IDs to do a structure read instead of having to request each individual attribute. I too by default start with the large connection size and only fall back to the standard if it fails - I think I remember a discussion here about making pylogix do the same?

dmroeder commented 4 years ago

@AndrewStuhr , I have made some changes that bundle multiple "data type discovering" requests into a single packet (multi-service messages). This should speed up reading many unique tags when you uses lists/tuples of tags passed to Read().

And yes @ottowayi , I did make the change relatively recently that you and @kyle-github suggested to default to the large connection size, so if the PLC supports it, the reads will support this.

I have experimented with using instance ID's in the past, but I don't plan on adding this at this point. Try v0.7.0, see if performance is improved for you. For reading multiple non-array tags, use lists, for arrays, read them individually, this will get you the best performance.