dmroeder / pylogix

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

Time to read/write to PLC is 10-20ms. Is this normal? #30

Closed alexvto closed 5 years ago

alexvto commented 6 years ago

The time to read/write an individual tag on the PLC is between 10 and 20 ms. Is this normal? What would be considered optimal? I am running on a quad core Intel i5 laptop

alexvto commented 6 years ago

I need to read 300 tags and write to 500 tags in one step. This means the read takes 3 s and the write close to 10 s. These are individual tags (input and output tags) not part of an internal PLC tag array.

Thank you

alexvto commented 6 years ago

This is a Rockwell PLC

dmroeder commented 6 years ago

That is definitely possible when reading/writing unique tags each time.

When a tag is accessed for the first time, 2 exchanges happen. First, the data type is requested, then the value is requested. Once the data type is known, we can just request the the value. This adds a little overhead when the tag is read for the first time. Doing this makes it easier on the user because they don't have to specify the data type up front.

In my tests, 20ms is a bit high, I get around 9 or 10 ms on the first read (which would be every read for you). I'm typically testing on a wireless network, you may get a little better performance when using a wired connection.

Out of curiosity, what is the specific part number of the PLC you are working with?

alexvto commented 6 years ago
 ProductName/Code - b'1756-ENBT/A'(58)

 Vendor/DeviceID  - Rockwell Automation/Allen-Bradley(12)

 Revision/Serial  - 6.60x39d318

I also got 10ms, on a wired connection -- after stripping some code from Read() and Write() in pylogix_eip (namely the part that checks whether the arg is a list ... etc):

def FastRead(self, tag):

return _readTag(self, tag, 1)

READ INDIVIDUAL OUT TAG O_024_04 ONCE = 0:00:00.021000

FAST READ INDIVIDUAL OUT TAG O_024_04 ONCE = 0:00:00.010

WRITE ONE INDIVIDUAL PLC INPUT TAG I_000_00 ONCE = 0:00:00.020

FAST WRITE ONE INDIVIDUAL PLC INPUT TAG I_000_00 ONCE = 0:00:00.011

I am on my work laptop, a 2015 ThinkPad Core i5 HQ.

I have to read/write to three PLCs that each have around 500 input tags and 300 output tags. I need to read all the PLC output tags, perform one simulation step and write to all the PLC input tags.

At 10ms / tag read/written this means 8 sec to read and write all the tags of one PLC - and to perform one simulation step.

I have around 100 steps to complete one simulation batch, so this comes to 800-1000 secs / batch -- which is a tad long.

I tried to multithread the reads (the fast reads) but it was worse than before: it came to 20ms / tag using the same FastRead method, instead of 10ms/tag when done serially. I would guess that the threading overhead is the problem.

Regards,Alex Voineaalex.voinea@gmail.com alex.voinea@gmail.com

On Wed, Nov 7, 2018 at 11:42 AM dmroeder notifications@github.com wrote:

That is definitely possible when reading/writing unique tags each time.

When a tag is accessed for the first time, 2 exchanges happen. First, the data type is requested, then the value is requested. Once the data type is known, we can just request the the value. This adds a little overhead when the tag is read for the first time. Doing this makes it easier on the user because they don't have to specify the data type up front.

In my tests, 20ms is a bit high, I get around 9 or 10 ms on the first read (which would be every read for you). I'm typically testing on a wireless network, you may get a little better performance when using a wired connection.

Out of curiosity, what is the specific part number of the PLC you are working with?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/dmroeder/pylogix/issues/30#issuecomment-436690983, or mute the thread https://github.com/notifications/unsubscribe-auth/Aqwo3bLNFaLOFcusvJMxqnV_W13nZ80xks5usw1qgaJpZM4YSR-y .

dmroeder commented 6 years ago

Thanks for the feedback, I'll look into your results.

Have you tried grouping your tags in lists to read? For example:

tagGroup1 = ['tag1', 'tag2', 'tag3', 'tag4'] values = test.Read(tagGroup1)

This would save you some time with each read. For now, you just need to ensure that you don't exceed the ~500 byte packet limit. This doesn't help you with writes though, but will cut some time with the reads.

dmroeder commented 6 years ago

Ok, I did some quick testing. I used wireshark to measure times. I was testing over a wireless network.

1000 individual reads of unique tags took ~14 seconds breaking up the 1000 tags in to lists of 10 tags and reading the lists took ~8 seconds edit: For my threads, I did 10 threads, each reading 100 individual tags, it took ~3 seconds.

Regarding reading individual tags and using your suggestion on FastRead made no difference for me, as I would have expected, removing those ~12 lines of code would be irrelevant in CPU time scale.

alexvto commented 6 years ago

You're right, I rewrote the threading w/o threads pool and I achieved 5-6 ms/tag read or write for 20 threads @ 50 reads or writes each -- on my laptop.

The interesting part is that I got the PLC engineers to map their individual tags (800+ tags / PLC) to a tag array, so instead of 800 individual reads I can do one array read or one array write with _writeTagArray in 50ms.

I have a question though: where does the 500 bytes limit for a read or a write come from ?

Regards,Alex Voineaalex.voinea@gmail.com alex.voinea@gmail.com

On Wed, Nov 7, 2018 at 4:04 PM dmroeder notifications@github.com wrote:

Ok, I did some quick testing. I used wireshark to measure times. I was testing over a wireless network.

1000 individual reads of unique tags took ~14 seconds breaking up the 1000 tags in to lists of 10 tags and reading the lists took ~8 seconds Using the same 10 lists of 10 tags and threads, it took ~2 seconds.

Regarding reading individual tags and using your suggestion on FastRead made no difference for me, as I would have expected, removing those ~12 lines of code would be irrelevant in CPU time scale.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/dmroeder/pylogix/issues/30#issuecomment-436776661, or mute the thread https://github.com/notifications/unsubscribe-auth/Aqwo3WL1flZugnn7iOFpdhQgHO_HmY4Gks5us0rKgaJpZM4YSR-y .

dmroeder commented 6 years ago

The 500 byte limit is in the CIP specification for this type of data exchange. I think some controllers can support larger packets, but I wanted to stick with what any controller would support.

Mapping the tags to an array is definitely a good way to get the time down. I should have mentioned that option first, a lot of people don't like modifying their PLC programs though (understandably).

The reason working with arrays is a lot faster is because you send one packet to get the data type of the tag, then one request for the values, the controller responds with however many packets it takes to get the values to you. You can fit quite a bit of information in a packet, so there are far fewer socket exchanges. Run wireshark and compare the difference.

I was thinking that I may be able to make a change where you could specify the data type up front and save the time it takes to request the data type.

alexvto commented 6 years ago

I think that would help a lot. In any case, thank you!

On Fri, Nov 9, 2018, 15:44 dmroeder <notifications@github.com wrote:

The 500 byte limit is in the CIP specification for this type of data exchange. I think some controllers can support larger packets, but I wanted to stick with what any controller would support.

Mapping the tags to an array is definitely a good way to get the time down. I should have mentioned that option first, a lot of people don't like modifying their PLC programs though (understandably).

The reason working with arrays is a lot faster is because you send one packet to get the data type of the tag, then one request for the values, the controller responds with however many packets it takes to get the values to you. You can fit quite a bit of information in a packet, so there are far fewer socket exchanges. Run wireshark and compare the difference.

I was thinking that I may be able to make a change where you could specify the data type up front and save the time it takes to request the data type.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/dmroeder/pylogix/issues/30#issuecomment-437489598, or mute the thread https://github.com/notifications/unsubscribe-auth/Aqwo3aqmYOoselPpAkYaDTfQmOo_vGqXks5utek1gaJpZM4YSR-y .

dmroeder commented 5 years ago

I finally pushed a change today that allows you to specify the data type when reading/writing. This will eliminate the request for the data type the first time a unique tag is read. It is an optional parameter and technically only needs to be provided on the first read of the tag (though it can be specified any read after without issue).

alexvto commented 5 years ago

Thank you !

I'll get it and re-test my code.

I think I told you earlier, I got the PLC guys to mirror the individual I/O tags into a couple of tag arrays -- so now I am reading/writing from/to those tag array, using just one read call @10ms and a few socket writes -- 20-30 ms for all the writes.

They agreed because it did not change the PLC functionality, they just added two tag arrays and mirrored existing tags in there.

Thank you DM !Alex Voineaalex.voinea@gmail.com alex.voinea@gmail.com

On Mon, Dec 10, 2018 at 6:04 PM dmroeder notifications@github.com wrote:

I finally pushed a change today that allows you to specify the data type when reading/writing. This will eliminate the request for the data type the first time a unique tag is read. It is an optional parameter and technically only needs to be provided on the first read of the tag (though it can be specified any read after without issue).

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/dmroeder/pylogix/issues/30#issuecomment-446008061, or mute the thread https://github.com/notifications/unsubscribe-auth/Aqwo3UgBHu3cb5-15Mj2cRX64cDBR8l5ks5u3uhygaJpZM4YSR-y .

dmroeder commented 5 years ago

Yeah I did catch that. Putting the data into arrays and reading/writing is the most efficient. I ended up making the change anyway since there are people that may not have the luxury of being able to do what you did.

Dealing with arrays will still be much faster than utilizing the change that I made.