3.2. Development

The following sub section explain in brief detail on how to setup a development environment. Eclipse SCADA is developed using the Eclipse Java IDE (JDT), still it possible to use other IDEs and development environments as well in order to use Eclipse SCADA in your own applications.

Eclipse SCADA is built using Eclipse Tycho. It is a set maven plugins, which extend maven in a way that the Eclipse PDE type projects can be used without much additional changes for maven.

This works extremely well in the Eclipse PDE enviroment, but if you want to develop a plain maven project you would need to import the artifacts manually, and Eclipse SCADA has a lot [2] of bundles.

The P2 repositories (which is the output of the Eclipse Tycho build) are converted to plain maven 2 repositories at the end of the build process. Dependencies are generated based on the validating target platform of the build. This might not be an optimal solution, but creates a working setup without much additional effort.

Dependencies to external projects, also the dependencies to Eclipse Orbit, are replaced with their Maven Central counterparts (as long as they have some). These artifacts are also excluded from the mirror process and not served from download.eclipse.org .

The URI to the maven 2 repositories contains the build id. So there is no composite maven repository at the moment.

The URI is: http://download.eclipse.org/eclipsescada/downloads/org.eclipse.scada/drops/[version]/[buildId]/maven/

version

The main version (e.g. 0.2.0)

buildId

The ID of the build (e.g. R201501260629)

So in order to use 0.2.0 using plain maven, adding the following repository setup will do:

...
<repository>
  <id>eclipse.scada</id>
  <url>http://download.eclipse.org/eclipsescada/downloads/org.eclipse.scada/drops/0.2.0/R201501260629/maven/</url>
  <releases>
    <enabled>true</enabled>
  </releases>
</repository>
...

[Note]Nightly builds

If you reference a nightly build (N), the versions will be -SNAPSHOT versions and you will need to also enable snapshots for the repository.

Only the two most recent nightly builds well be kept. Therefore linking to a nightly release might not be a good idea since it might be gone pretty soon.

Eclipse SCADA uses Apache Mina for most of its communication protocols. Apache Mina uses the maven plugin org.apache.felix:maven-bundle-plugin for providing OSGi bundles. So as soon as you declare a dependency to some Eclipse SCADA module which has a dependency to Apache Mina, you will need to add this plugin to you setup as well.

The reason behind that is, that the maven-bundle-plugin creates its artifacts with the suffix .jar, since OSGi bundles are actually JAR files. However it defines the packaging type bundle in the maven POM file of Apache Mina. Now as soon as a dependency is declared on an Apache Mina project, Maven tries to resolve that dependency using the suffix .bundle, due to the packaging type bundle. But that file does not exists, since the correct file extension for OSGi bundles is .jar.

Adding the maven-bundle-plugin to the build takes care of this behavior. The plugin automatically rewrites these requests internally from .bundle to .jar and everything works as expected.

The following fragment adds the necessary plugin to the build:

...
<plugins>
  <plugin>
    <groupId>org.apache.felix</groupId>
    <artifactId>maven-bundle-plugin</artifactId>
    <extensions>true</extensions>
  </plugin>
</plugins>
...

A complete minimal project might look like:

<project
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>org.eclipse.scada</groupId>
  <artifactId>client-sample1</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <name>Eclipse SCADA Client Sample #1</name>
  <description>A sample client</description>

  <repositories>
    <repository>
      <id>eclipse.scada</id>
      <url>http://download.eclipse.org/eclipsescada/downloads/org.eclipse.scada/drops/0.2.0/R201501260629/maven/</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </repository>
  </repositories>

  <dependencies>
    <dependency>
      <groupId>org.eclipse.scada.core</groupId>
      <artifactId>org.eclipse.scada.da.client.ngp</artifactId>
      <version>[0.1.0,)</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.1.1</version>
    </dependency>
  </dependencies>

  <!-- this is required for Apache Mina -->

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <extensions>true</extensions>
      </plugin>
    </plugins>
  </build>
</project>
			

Also see https://wiki.eclipse.org/EclipseSCADA/GettingStarted/WithMaven for additional information.

package org.eclipse.scada.eclipsemagazin;
 
import java.util.Observable;
import java.util.Observer;
 
import org.eclipse.scada.core.ConnectionInformation;
import org.eclipse.scada.core.client.AutoReconnectController;
import org.eclipse.scada.core.client.ConnectionFactory;
import org.eclipse.scada.core.client.ConnectionState;
import org.eclipse.scada.core.client.ConnectionStateListener;
import org.eclipse.scada.da.client.Connection;
import org.eclipse.scada.da.client.DataItem;
import org.eclipse.scada.da.client.DataItemValue;
import org.eclipse.scada.da.client.ItemManagerImpl;
 
public class SampleClient {
 
  public static void main(String[] args) throws InterruptedException {
    // the ConnectionFactory works a bit like JDBC,
    // every implementation registers itself when its loaded
    // alternatively it is also possible to use the connection
    // directly, but that would mean the code would have to be aware
    // which protocol is used, which is not desirable
    try {
      Class.forName("org.eclipse.scada.da.client.ngp.ConnectionImpl");
    } catch (ClassNotFoundException e) {
      System.err.println(e.getMessage());
      System.exit(1);
    }
 
    final String uri = "da:ngp://localhost:2102";
 
    final ConnectionInformation ci = ConnectionInformation.fromURI(uri);
 
    final Connection connection = (Connection) ConnectionFactory.create(ci);
    if (connection == null) {
      System.err.println("Unable to find a connection driver for specified URI");
      System.exit(1);
    }
 
    // just print the current connection state
    connection.addConnectionStateListener(new ConnectionStateListener() {
      @Override
      public void stateChange(org.eclipse.scada.core.client.Connection connection, ConnectionState state, Throwable error) {
        System.out.println("Connection state is now: " + state);
      }
    });
 
    // although it is possible to use the plain connection, the
    // AutoReconnectController automatically connects to the server
    // again if the connection is lost
    final AutoReconnectController controller = new AutoReconnectController(connection);
    controller.connect();
 
    // although it is possible to subscribe to an item directly,
    // the recommended way is to use the ItemManager, which handles the
    // subscriptions automatically
    final ItemManagerImpl itemManager = new ItemManagerImpl(connection);
 
    final DataItem dataItem = new DataItem("memory-cell-0", itemManager);
    dataItem.addObserver(new Observer() {
      @Override
      public void update(final Observable observable, final Object update) {
        final DataItemValue div = (DataItemValue) update;
        System.out.println(div);
      }
    });
  }
}
package org.eclipse.scada.examples.modbus.exporter;

import org.eclipse.scada.da.server.browser.common.FolderCommon;
import org.eclipse.scada.da.server.common.MemoryDataItem;
import org.eclipse.scada.da.server.common.ValidationStrategy;
import org.eclipse.scada.da.server.common.impl.HiveCommon;

public class SampleHive extends HiveCommon
{
    private FolderCommon rootFolder;

    public SampleHive ()
    {
        setValidatonStrategy ( ValidationStrategy.FULL_CHECK );

        setRootFolder ( this.rootFolder = new FolderCommon () );
    }

    @Override
    protected void performStart () throws Exception
    {
        super.performStart ();

        MemoryDataItem mem1;
        registerItem ( mem1 = new MemoryDataItem ( "mem1" ) );
        this.rootFolder.add ( "mem1", mem1, null );
    }

    @Override
    public String getHiveId ()
    {
        return SampleHive.class.getName ();
    }

}

This sample hive features one item named mem1 which can be read and written. Data that gets written on the item will immediately be available as a change. However when the application is restarted the information will be lost and the item will again be initialized with null.



[2] A the moment of writing it is over 400 OSGi bundles/JAR files.