The JEasyOpc project provides a
OPC (OLE for process control) communication.
OPC is open connectivity in industrial automation and the enterprise systems that support the industry. Interoperability is assured through the creation and maintenance of non-proprietary open standards specifications.
The first OPC standard specification resulted from the collaboration of a number of leading worldwide automation suppliers working in cooperation with Microsoft. Originally based on Microsoft's OLE COM and DCOM technologies, the specification defined a standard set of objects, interfaces and methods for use in process control and manufacturing automation applications to facilitate interoperability.
The COM/DCOM technologies provided the framework for software products to be developed. There are now hundreds of OPC Data Access servers and clients. The JEasyOpc project wants to provide this technology for everyone with easy way.
The JEasyOpc project provides OPC for Java technology. The project is based on JDK 1.5 (or higher) and
a Delphi (2005) native code (JCustomOpc.dll library). The application uses
32-bit MS Windows (95/98/NT/2000/XP). The operating system Linux isn't supported.
The JEasyOpc project is established on LGPL (GNU Library or Lesser General Public License). The project
can be downloaded from
SourceForge.net as actual release (2.xx.xx) or night revision from SVN repository. The project is build on
Eclipse 3.2.x Open Source IDE.
The release is distributed as a zip-file for a quick download. In a zip-file, there are these important directories and files:
jeasyopc.jarsrc.jareclipse-project\JEasyOpc.zipdocresourceslibrary.path).
First, the resources directory has to be added to the CLASSPATH of your application.
For this example, you have to install
Matrikon OPC Server on your localhost.
Now, we can try first small example of JOpc usage.
package javafish.clients.opc;
import javafish.clients.opc.component.OpcGroup;
import javafish.clients.opc.component.OpcItem;
import javafish.clients.opc.exception.CoUninitializeException;
import javafish.clients.opc.exception.ComponentNotFoundException;
import javafish.clients.opc.exception.ConnectivityException;
import javafish.clients.opc.exception.SynchReadException;
import javafish.clients.opc.exception.UnableAddGroupException;
import javafish.clients.opc.exception.UnableAddItemException;
import javafish.clients.opc.variant.Variant;
public class SynchReadItemExample {
public static void main(String[] args) throws InterruptedException {
SynchReadItemExample test = new SynchReadItemExample();
JOpc.coInitialize();
JOpc jopc = new JOpc("localhost", "Matrikon.OPC.Simulation", "JOPC1");
OpcItem item1 = new OpcItem("Random.ArrayOfReal8", true, "");
OpcGroup group = new OpcGroup("group1", true, 500, 0.0f);
group.addItem(item1);
jopc.addGroup(group);
try {
jopc.connect();
System.out.println("JOPC client is connected...");
}
catch (ConnectivityException e2) {
e2.printStackTrace();
}
try {
jopc.registerGroups();
System.out.println("OPCGroup are registered...");
}
catch (UnableAddGroupException e2) {
e2.printStackTrace();
}
catch (UnableAddItemException e2) {
e2.printStackTrace();
}
synchronized(test) {
test.wait(50);
}
// Synchronous reading of item
int cycles = 7;
int acycle = 0;
while (acycle++ < cycles) {
synchronized(test) {
test.wait(1000);
}
try {
OpcItem responseItem = jopc.synchReadItem(group, item1);
System.out.println(responseItem);
System.out.println(Variant.getVariantName(responseItem.getDataType()) + ": " + responseItem.getValue());
}
catch (ComponentNotFoundException e1) {
e1.printStackTrace();
}
catch (SynchReadException e) {
e.printStackTrace();
}
}
JOpc.coUninitialize();
}
}
It's a example of synchronous reading of a item, specially Variant array. The OPC communication is based on
COM+ components (remote access using DCOM). First of all, you have to run coInitialize() method for
activation of COM processes. Last command for termination of all COM processes, you can use coUninitialize()
method. This finalization method is called only ONE, it's definitive termination.
The connection to OPC Server is ensured by a connect() method on JCustomOpc java component.
The disconnection method doesn't exist. Why? Because COM Server ensures the disconnection process automaticly. If OPC client
signs off its groups, the OPC Server disconnects connection after short timeout. Note, coUninitialize() method
releases all COM components and disconnect all connections.
In our example, we use JOpc java component. In our opinion, it is best way for usage of this library.
This component is intendeded for a inheritance. For example, the JEasyOpc is inherited from this component, and
extends asynchronous functionality (special thread for Asynch 2.0 control groups). See appropriate examples in the eclipse-project.
JOpc and its descendants are supporting asynchronous and synchronous modes of OPC transmission. For your better
imagination, we will describe component architecture in the next chapters.
Architecture of java components is shown on figure 1. The ancestor of all OPC components is JCustomOpc.
This class encapsulates native dll-library and fundamental part of DCOM communication. The OPC Browser JOpcBrowser
component is directly inherited from JCustomOpc. In other hand, JOpc is inherited from JCustomOpc too,
but adds all items and groups control, administration processes and communication modes. JOpc is a base class for
your inheritance. JEasyOpc is example of thread implenentation of asynchronous 2.0 mode. JEasyOpc uses
asynchronous messaging by OpcAsynchGroupListeners (group listeners). In addition, JEasyOpc supports
Jakarta Common Logging and a multi-language support.
JEasyOpc project exceptions are inherited from OpcException and OpcRuntimeException.
These exceptions are divided according to common features and usage. The usage of these exceptions is very
important for correct functionality of your application. In next figures 2., 3. and 4. are shown hierarchic tree
of these exceptions (NOTE: please click on figure 2. for better image quality).
JOpc implements support of OPC Groups (class OpcGroup) and OPC Items
(class OpcItem). OPCDA specifies registration of groups. The groups have to include
items, only items are impossible without groups. The java architecture is shown on figure 5.
The groups with items are flexible structure. The Item constructor is
/**
* Create new instance of OPCItem
*
* @param itemName String - specific Tag name of item
* @param active boolean - begin activity of item
* @param accessPath String - accessPath is the "how" for the server to get the
* data specified by the itemName (ItemID, the what). The client uses this function
* to identify the possible access paths for the specified ItemID (similary with namespaces).
* Not all Opc Serves support these access paths.
*/
public OpcItem(String itemName, boolean active, String accessPath) {
}
/**
* Create new instance of OPC Group
*
* @param groupName String (user identificatio name of group)
* @param active boolean - begin activity of group
* @param updateRate double - refresh time of group in milliseconds
* @param percentDeadBand float - see percentDeadBand definition:
*
* Deadband will only apply to items in the group that
* have a dwEUType of Analog available. If the dwEUType is Analog,
* then the EU Low and EU High values for the item can be used to
* calculate the range for the item. This range will be multiplied with
* the Deadband to generate an exception limit.
* An exception is determined as follows:
*
* Exception if (absolute value of (last cached value - current value) > pPercentDeadband * (EU High - EU Low) )
*
* If the exception limit is exceeded, then the last cached value is updated with the new value
* and a notification will be sent to the IAdviseSink (if any). The pPercentDeadband is an optional
* behavior for the server. If the client does not specify this value on a server that does
* support the behavior, the default value of 0 (zero) will be assumed, and all value changes
* will update the CACHE. Note that the timestamp will be updated regardless of wether the
* cached value is updated.
*/
public OpcGroup(String groupName, boolean active, int updateRate, float percentDeadBand) {
}
For more information, see JavaDoc of JEasyOpc project ./doc/apidocs/index.htm.
You can see the usage of groups in the next example:
OpcItem item1 = new OpcItem("Random.Real8", true, "");
OpcItem item2 = new OpcItem("Bucket Brigade.ArrayOfReal8", true, "");
OpcGroup group = new OpcGroup("group1", true, 500, 0.0f);
group.addItem(item1);
group.addItem(item2);
We created one group with two items. Items have tag-names: Random.Real8 and
Bucket Brigade.ArrayOfReal8. You can obtain names these tags by the help of JOpcBrowser
component. This component can download appropriate OPC tree structure and get information about tag-items.
These example items have activity of true and haven't any specfic accessPath (default).
The items are added to the group with user name: "group1". Group has activity on true,
update ratio is 500 ms and percentDeadBand is 0.0 (float).
The main attribute is update ratio (updateRate). This attribute determines a refresh of group on a server side.
For example, if you use synchronous reading with 50 ms, and updateRate was set on 500 ms, it's a wrong, because
updateRate has to be less than a time-reading. But the overmuch updateRate can load the OPC Server!
The updateRate has to be configure discreetnes.
In our example, we used item Bucket Brigade.ArrayOfReal8 (we use
Matrikon OPC Server for testing).
This item is read/write. OPC communication serves two-sided (client cas write data to server). It is not perfect, but
some reasons exist for this usage. In the last example, we will set our Variant array to the item. This item can be send
to the server after that.
// prepare variant list (Double Variant Array = VT_R8) VariantList list = new VariantList(Variant.VT_R8); list.add(new Variant(1.0)); list.add(new Variant(2.0)); list.add(new Variant(3.0)); Variant varin = new Variant(list); // set value to item2 item2.setValue(varin);
NOTE: In asynchronous mode, the group has a register method addAsynchListener(asynchGroupListener)
for a registration of its groupListeners. The group listener gets actual downloaded a OpcGroup
(rather a clone of group). The example is shown in JEasyOpcExample.java in JEasyOpc project.
From version 2.2.0 and higher, the Variant type is supported. The value of item is represented by this variant. This java representation of Variant type corresponds with Variant of OLE and COM technologies. Not exactly, but it keeps compatibilty many data types. This usage brings advantages and disadvantages of Variant types.
The Variant type can contain a arbitrary data type. For example, we can create variant with double value:
// preparation of variant (Double Variant = VT_R8) Variant varin = new Variant(1.0); // get information about data type System.out.println(Variant.getVariantName(varin.getVariantType()));
It is easy, the variant can save any data type. But which getMethod do you use for appropriate value?
It is a question. You can use method getVariantType() for detect a correct Variant type. Or you know
the output type, or you can all types transform to String (with method toString()).
In our opinion, it is a main problem of variant types.
The Variant UML structure is shown on figure 6. You can see all variant types and appropriate contructors for
variant usage (class Variant). The Variant has fully implemented methods:
equals, hashcode, clone and compareTo.
The logging system is based on
Jakarta Common Logging (JCL).
JCL provides a Log interface that
is intended to be both light-weight and an independent abstraction of other logging toolkits.
It provides the middleware/tooling developer with a simple
logging abstraction, that allows the user (application developer) to plug in
a specific logging implementation.
JCL provides thin-wrapper Log implementations for other logging tools, including
Log4J,
Avalon LogKit
(the Avalon Framework's logging infrastructure),
JDK 1.4, and an implementation of JDK 1.4 logging APIs (JSR-47) for pre-1.4 systems.
The interface maps closely to Log4J and LogKit.
Familiarity with high-level details of the relevant Logging implementations is presumed.
JCL provides only a bridge for writing log messages. It does not (and will not) support any sort of configuration API for the underlying logging system.
The Configuration of the behavior of the JCL ultimately depends upon the logging toolkit being used. Please consult the documentation for the chosen logging system.
Log4J is a very commonly used logging implementation (as well as being the JCL primary default), so a few details are presented herein to get the developer/integrator going. Please see the Log4J Home for more details on Log4J and it's configuration.
Configure Log4J using system properties and/or a properties file:
LogFactory.getLog(logger.name),
used to create the logger instance. Priorities are:
DEBUG,
INFO,
WARN,
ERROR,
or FATAL.
log4j.logger.org.apache.component=DEBUG
will enable debug messages for all classes in both
org.apache.componentand
org.apache.component.sub.
Likewise, setting log4j.logger.org.apache.component=DEBUG
will enable debug message for all 'component' classes,
but not for other Jakarta projects.
Release notes:
JEasyOpc project release ------------------------ Includes - Synch read/write opc-items - Synch reading of groups - Asynch reading opc-groups (1.0 and 2.0) - Controls activity and updateTime of groups and items - Handles all exceptions from native code - Hierarchic exceptions system - OPC Browser and OPCEnum components - Multilanguages support - Logging system: Usage of Jakarta Common Logging + log4j - JUnit tests and many examples - Example: JEasyOPC -> asynch opc-client implementation - OPC Data Types, support of Variant type - Support of Variant arrays transmission (read/write) - Asynchronous listeners on OPC Group instances - User manual
The usage of JEasyOpc library is shown in our examples and JUnit tests in a eclipse-project:
jeasyopc-2.x.x.zip//eclipse-project/JEasyOpc.zip//src/examples (or /junit)
https://svn.sourceforge.net/svnroot/jeasyopc/JEasyOpc/src/examples (or /junit)