In english: This FAQ; lightopc.h and README.txt in sources. That's all.
In russian only: The manual (gzip).
The ldReadTags() is called only for OPC_DS_DEVICE operations. Most of OPC clients uses OPC_DS_CACHE rather than perform device reading.
The cache reading is handled completely inside LightOPC as well as OnDataChange() update generation and deadband calculations.
Therefore you have to use either loCacheXXXX() to keep the cache actual. In fact, if the ldReadTags() performs real reading it should update the cache by an explicit call to loCacheUpdate().
Usage ldReadTags() is optional. It useful to implement a "destructive" reading. It guarantees the athomicity and the ordering of DEVICEs' requests.
Probably nothing to do. You have to initialize ldRefreshRate according your system performance and device's requirements. The reasonable range is 30...1000 mS. The LightOPC does all updates for the clients internally.
Usally a client makes a lot of different update rates for different groups. And there are bunch of different clients might be connected. And the LightOPC library cut them off to to protect the driver from touching by each OnDataChange() invocation.
The UpdateRate specified by a client used to schedule OnDataChange notifications. Such notifications performed using cached data becuse by definition the OnDataChange logic is operate on the cached data and may not affect the device state. The driver have to place the data into the cache by using loUpdateCache() or loLockCache() / loUnlockCache(). These cached data are also used to satisfy the clients' Read() / Refresh() requests with OPC_DS_CACHE attribute set.
Thus your driver should keep the cache up to date and may not worry about client's Update Rate.
In many cases it's desirable to preserve the frequency of devices' polling unrelated to number of clients connected and their update rates.
The driver may call loCacheUpdate() as often as required by underlying hardware. The function is especially designed to be fast and be usable in time-critical loops. It doesn't make inpredictable delays. If an extra call to VariantCopy() seems too slow for you then try loCacheLock() / loCacheUnlock().
This technique allows you to satisfy your device's timings easily and don't pay much attention for clients' requests.
There are two ways to reduce devices' polling frequency:
Probably you wrote somting like
loServiceCreate(&my_service, &ld, XXX);
Where the XXX is the total amount of available tags. Simply increase this number.
The total of tags can not be changed without loService recreating due to performance reasons.
Once a tag added it can not be deleted. Therefore in general case you have to restart the server. But...
IMHO the dynamically created tags cause problems for clients. In most cases it's desirable to have a tag browseable even it isn't connected to a data source at the moment. It simplify clients' setup.
loRealTag rt parameter of loAddRealTag() is optional.
LightOPC make no use of it, rather it will be passed back to your code in
You may define pointer to your private data and pass it to loAddTagXXX() as (loRealTag)rt argument. You're free even define your own struct loRealTag_ to avoid pointer casting.
Define the ldAskItemId() callback for mapping derived names to unnamed tags.
Create a "hint" tag with desired name (like "Reg[0...32727]") and NULL or VT_EMPTY tdValue or loTF_EMPTY specified. This bogus tag will be visible for BrowseAddresSpace only.
Create required amount of nameless tags (with NULL or empty tdName).
Make distinction of the IOPCSecurity interface and OPC Security in general. The LightOPC conforms to several security issues.
The IOPCSecurity isn't provided directly because this silly interface doesn't provide any useful service. But it can be implemented easily and attached to loClient via loClientChain().
In general a "security" consists of authentication and authorization. The IOPCSecurity doesn't provide authorization stuff (i.e. no access rights to userid map).
In the simplest case you may split your tags to few sets and serve them via different servers with different DCOM authorization. These servers can be run in one process.
To make your server secured at items level you have to do following things:
Oh... The LightOPC itself is "freethreaded" as a bird.
For a out-of-proc server the model of the thread supporting IClassFactory is significant. The COINIT_MULTITHREADED is seems preferable. The COINIT_APARTMENTTHREADED is also acceptable, but such thread must implement a message loop and process windows messages.
In order to utilize threading models other than "free" you also have to specify (loDf_BOTHMODEL | loDf_FREEMARSH) in loDriver::ldFlags instead of loDf_FREEMODEL.
To show excellent performance for a singlethreaded client connected to inproc server the BOTH model supported. Support for BOTH model should be enabled (or disabled) at compile time (by LO_USE_BOTHMODEL and LO_USE_FREEMARSHALL) and at the run time (by loDf_BOTHMODEL and loDf_FREEMARSH). Because the BOTH model may slowdown freethreaded clients it's possible to enable it for arbitrary clients connections.
The loDf_BOTHMODEL & loDf_FREEMARSH forces some overhead and local message loops in lightopc library. Thus it makes a server less tolerant to clients' faults.
Are you really confused by benchmarks?
We're not a member of OPC Foundation and probably never become. Thus we haven't Automation Wrapper DLL and didn't perform compliance tests.
The executables of opcXXXauto.dll are included in the free downloads from many commercial vendors. For example Matrikon Explorer & Simulator, KEPware, etc.
We've tested LightOPC with such automation wrappers.
We haven't it.
IMHO the OPC provides a client oriented API and writing a client on top of bare OPC is quite easy.
Look at following links, they can be useful:
Before call to
loServiceCreate() set the appropriate
loDriver::ldRefreshRate = 1 or another fraction of 1000 ms.
See lightopc.h for details about
Contact: master AT ipi ac ru