Hmm...
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:
poll for active tags only. These tags can be tracked via loDriver::ldSubscribe().
update tags by interrupts if devices allowed that.
The interrupt-driven tags should seems "fresh" in the cache. To ensure that you have to update periodically the timestamp of such tags. The loCacheUpdate() can set arbitrary timestamp for several tags very quickly.
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...
It's possible to add tags to a running server as much as specified in loCreateService().
It's possible to create unnamed tags and assign them different names dynamically using ldAskItemId(). Unfortunately, such tags will not be shown by BrowseAdressSpace and their types and other attributes can not be changed.
It's possible to create separate server for each device if devices might appear and disappear at any time.
Finally, server can be restarted gracefully. It's possible to run several loService in the single process and replace an obsolete server by another without process' restarting.
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.
The
loRealTag rt
parameter of loAddRealTag() is optional. LightOPC make no use of it, rather it will be passed back to your code in loTagPair::tpRt.
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:
Catch the action requested by an OPC client.
You're able to catch following actions:
Any writing by catching ldWriteTags().
Device reading by catcing ldReadTags().
Connecting a client to a tag by hooking ldAskItemId() & loDF_CHECKITEM / loTF_CHECKITEM. In this case you may prohibit AddItems() any access (either cache or device) for a particular client - tag combination.
Identify the client. It's possible in several ways:
Obtain the caller's context through usual DCOM practice.
Verify item's access_path for a magic key.
Make 2 (userid / password) or even 3 (challenge / userid / response) additional per-client tags. A client have to initialize them properly before access anything else.
Check permissions and resume (or reject) the catched action. You're free to use either native NT's security (which is noticeable slow for 100 or more accounts) or a private ACL.
That's all.
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?
The EventLog is extremely slow. Set the lower log level or redirect the logs to files. See README.txt for details about unilog tunings.
In case of extremely many named tags try to replace them by unnamed ones and "hint strings".
For an in-proc server choose right threading model.
Try to force clients to accept canonical datatype.
Increase ldRefreshRate and make it multiple of system's scheduler granularity.
Make no use of loDF_IGNCASE, xmalloc() & Co, LO_USE_OBJXREF.
Try to disable OPC_READABLE / OPC_WRITEABLE checking via LO_CHECK_RIGHTS, but it will not help much.
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:
http://lhcb-comp.web.cern.ch/lhcb-comp/ECS/opcevaluation/opcclienttutorial/
ftp://ftp.mysst.com/ pub/beta/ client.zip
Before call to
loServiceCreate()
set the appropriate
loDriver::ldRefreshRate
= 1 or another fraction of 1000 ms.
See lightopc.h for details about
ldRefreshRate
.
ftp://ftp.ipi.ac.ru/pub/LightOPC/ unilog-current.tgz
联系客服