Introduction

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.

Quick Start

Configuration

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.jar
the final library for usage in your application.

src.jar
the source of library for a preview of library classes.

eclipse-project\JEasyOpc.zip
zip-file with whole JEasyOpc project for Eclipse. There are all examples, JUnit tests, all sources!

doc
the directory includes documentation.

resources
the configuration files of JEasyOpc library. This resources have to be included in CLASSPATH of your project. There are all important information about usage of logging, internationalization and dll-library path (property library.path).

First Launch

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

Components

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.

Exceptions

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).

OPC Items and Groups

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) {
  }
The group constructor is defined as
  /**
   * 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.

Variant type

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.

Logging system

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.

Configuring The Underlying Logging System

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.

Configuring Log4J

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:

  • log4j.configuration=log4j.properties Use this system property to specify the name of a Log4J configuration file. If not specified, the default configuration file is log4j.properties.
  • log4j.rootCategory=priority [, appender]*
  • Set the default (root) logger priority.
  • log4j.logger.logger.name=priority Set the priority for the named logger and all loggers hierarchically lower than, or below, the named logger. logger.name corresponds to the parameter of LogFactory.getLog(logger.name), used to create the logger instance. Priorities are: DEBUG, INFO, WARN, ERROR, or FATAL.

    Log4J understands hierarchical names, enabling control by package or high-level qualifiers: 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.
  • log4j.appender.appender.Threshold=priority
  • Log4J appenders correspond to different output devices: console, files, sockets, and others. If appender's threshold is less than or equal to the message priority then the message is written by that appender. This allows different levels of detail to be appear at different log destinations. For example: one can capture DEBUG (and higher) level information in a logfile, while limiting console output to INFO (and higher).

Usage of JEasyOpc library

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)