I am working in a single-thread OPC client program managing 3 different Siemens PLCs connected to the same OPC Server, also by Siemens.
The single-threaded client looks like this:
loop
begin
processPLC1;
processPLC2;
processPLC3;
end;
Each processPLC procedures make calls to the underlying OPC library, such as:
OPCutils.WriteOPCGroupItemValue(FGroupIf, FHandleItemOpc, Value);
Ok, now I want to call each processPLC in a different thread and work in parallel.
I did some research and started some code using OmniThreadLibrary, but I don't think the OPC code is multithread-safe. Is it?
Should I use task.Invoke or something like that? How about ReadOPC functions, which return the value of the PLC tag? What would be the best practices here?
Thank you!!!
IMHO it seems like you are overengineering it, it is better to keep things as simple as possible but adding threads will not exactly make things simpler.
If you would have had multiple OPC servers then threads would maybe been a better abstraction but now you need only one connection to one OPC server then having multiple threads and multiple connections seems overly complicated without much real-world gain.
Instead use the regular OPC subscription mechanisms for your reads and write sequentially as you did before.
OPC is based on COM technology, so the same rules apply. If you want to access the OPC server from different threads, each thread has to call CoInitialize and CoUninitialize on its own.
It is most likely the same as accessing the OPC server from different processes. Whether the OPC server itself is single- or multi-threaded doesn't matter.
What might get in your way is the abstraction of the OPC client library you are using.
I've generally seen this done in two ways :
1) Application has a single OPC client instance owned by a single thread. All parallel processes being automated by the client application then use some sort of messaging or synchronization with the thread owning the OPC client when reading/writing OPC values.
2) Each thread owns its own private OPC client, each of which communicates independently with the OPC server.
Personally, I've found that the most used scheme is the former; one OPC (or other proprietary) client object with threads making synchronized calls to the client. Really, in almost all process control situations you are multithreading for the purpose of elegantly encapsulating a logical real-world task and segregating it from the UI, not at all for any sort of performance. Those threads can afford to block for comparatively "long" periods waiting for data - the PLC will happily hold down the fort for a hundred milliseconds if it needs to.
Which one you choose depends largely on the scale and nature of your application. For lots of light-weight threads which spend a long time waiting for external real-time events it makes sense to keep a single OPC client instance in the application and save the overhead of a large number of independent connections. For a small number of heavy, fast moving, OPC intensive threads it maybe makes sense to give each one its own OPC client instead.
Also keep in mind the refresh rate of your OPC tags - lots of times the server is only updating them on the order of every ~100ms or so. It's probably taking your PLC at least 10ms just to perform a single scan, even. It doesn't make sense to have a huge number of threads independently polling the server a hundred times per second when the data is never going to refresh that quickly.
For process control software what you really want is to have a lot of idle or low-load CPU time - the lighter your threads are the better. Total system responsiveness is key and the ability to have your software handle high load situations (suddenly a large number of tasks converge while the OS decides it is time to index the HDD... headroom keeps the cogs greased, so to speak). Most of your threads should probably be just waiting most of the time. Events and callbacks generally make most sense here.
Also, thinking about the PLC programming is important here too. For example, in my machines I have a few very time-critical operations that are, at the same time, uniquely timed every time they are run - these are processes that are on the order of minutes in duration, timed to under a tenth of a second or better, repeated hundreds to thousands of times per day, and are of a critical but different duration each time they are run. I've seen these handled two ways - one in software, one in the PLC. For the former case the software tells the PLC when to start and then it goes until the software tells it to stop. This has obvious pitfalls; it's much better in this case to simply send the time interval to the PLC and let it do the timing. Suddenly all time/polling pressure is taken away from the software and the process can gracefully handle things like the automation computer crashing, etc. In cases where you are tempted to put heavy pressure on your OPC server for time-critical data it often pays to re-evaluate the design of the whole system - software and PLC.