Add the Utgard resources

This commit is contained in:
luoyan35714
2014-12-10 17:42:02 +08:00
parent fd77b8e66e
commit 6d72a9b91e
137 changed files with 11245 additions and 230 deletions
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry combineaccessrules="false" kind="src" path="/org.openscada.opc.dcom"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>
+2
View File
@@ -0,0 +1,2 @@
bin/
/target
+46
View File
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openscada.opc.lib</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.babel.editor.rbeBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.babel.editor.rbeNature</nature>
<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
</natures>
</projectDescription>
+165
View File
@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
+1
View File
@@ -0,0 +1 @@
source.. = src
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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.openscada.utgard</groupId>
<artifactId>org.openscada.opc.lib</artifactId>
<version>1.1.0.v20130529</version>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.kohsuke.jinterop</groupId>
<artifactId>j-interop</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>org.openscada.opc.dcom</groupId>
<artifactId>org.openscada.opc.dcom</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,30 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.common;
public class AlreadyConnectedException extends Exception
{
/**
*
*/
private static final long serialVersionUID = -6494637563117314114L;
}
@@ -0,0 +1,149 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.common;
/**
* Holds the connection information
* @author Jens Reimann <jens.reimann@th4-systems.com>
*
* If both <code>clsId</code> and <code>progId</code> are set then <code>clsId</code>
* has priority!
*/
public class ConnectionInformation
{
private String _host = "localhost";
private String _domain = "localhost";
private String _user = "";
private String _password = "";
private String _clsid = null;
private String _progId = null;
public ConnectionInformation ()
{
super ();
}
public ConnectionInformation ( final String user, final String password )
{
super ();
this._user = user;
this._password = password;
}
public ConnectionInformation ( final ConnectionInformation arg0 )
{
super ();
this._user = arg0._user;
this._password = arg0._password;
this._domain = arg0._domain;
this._host = arg0._host;
this._progId = arg0._progId;
this._clsid = arg0._clsid;
}
public String getDomain ()
{
return this._domain;
}
/**
* Set the domain of the user used for logging on
* @param domain
*/
public void setDomain ( final String domain )
{
this._domain = domain;
}
public String getHost ()
{
return this._host;
}
/**
* Set the host on which the server is located
* @param host The host to use, either an IP address oder hostname
*/
public void setHost ( final String host )
{
this._host = host;
}
public String getPassword ()
{
return this._password;
}
public void setPassword ( final String password )
{
this._password = password;
}
public String getUser ()
{
return this._user;
}
public void setUser ( final String user )
{
this._user = user;
}
public String getClsid ()
{
return this._clsid;
}
public void setClsid ( final String clsid )
{
this._clsid = clsid;
}
public String getProgId ()
{
return this._progId;
}
public void setProgId ( final String progId )
{
this._progId = progId;
}
public String getClsOrProgId ()
{
if ( this._clsid != null )
{
return this._clsid;
}
else if ( this._progId != null )
{
return this._progId;
}
else
{
return null;
}
}
}
@@ -0,0 +1,30 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.common;
public class NotConnectedException extends Exception
{
/**
*
*/
private static final long serialVersionUID = -3745147771605524635L;
}
@@ -0,0 +1,359 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.jinterop.dcom.common.JIException;
import org.openscada.opc.lib.common.NotConnectedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AccessBase implements ServerConnectionStateListener
{
private static Logger logger = LoggerFactory.getLogger ( AccessBase.class );
protected Server server = null;
protected Group group = null;
protected boolean active = false;
private final List<AccessStateListener> stateListeners = new CopyOnWriteArrayList<AccessStateListener> ();
private boolean bound = false;
/**
* Holds the item to callback assignment
*/
protected Map<Item, DataCallback> items = new HashMap<Item, DataCallback> ();
protected Map<String, Item> itemMap = new HashMap<String, Item> ();
protected Map<Item, ItemState> itemCache = new HashMap<Item, ItemState> ();
private int period = 0;
protected Map<String, DataCallback> itemSet = new HashMap<String, DataCallback> ();
protected String logTag = null;
protected Logger dataLogger = null;
public AccessBase ( final Server server, final int period ) throws IllegalArgumentException, UnknownHostException, NotConnectedException, JIException, DuplicateGroupException
{
super ();
this.server = server;
this.period = period;
}
public AccessBase ( final Server server, final int period, final String logTag )
{
super ();
this.server = server;
this.period = period;
this.logTag = logTag;
if ( this.logTag != null )
{
this.dataLogger = LoggerFactory.getLogger ( "opc.data." + logTag );
}
}
public boolean isBound ()
{
return this.bound;
}
public synchronized void bind ()
{
if ( isBound () )
{
return;
}
this.server.addStateListener ( this );
this.bound = true;
}
public synchronized void unbind () throws JIException
{
if ( !isBound () )
{
return;
}
this.server.removeStateListener ( this );
this.bound = false;
stop ();
}
public boolean isActive ()
{
return this.active;
}
public void addStateListener ( final AccessStateListener listener )
{
this.stateListeners.add ( listener );
listener.stateChanged ( isActive () );
}
public void removeStateListener ( final AccessStateListener listener )
{
this.stateListeners.remove ( listener );
}
protected void notifyStateListenersState ( final boolean state )
{
final List<AccessStateListener> list = new ArrayList<AccessStateListener> ( this.stateListeners );
for ( final AccessStateListener listener : list )
{
listener.stateChanged ( state );
}
}
protected void notifyStateListenersError ( final Throwable t )
{
final List<AccessStateListener> list = new ArrayList<AccessStateListener> ( this.stateListeners );
for ( final AccessStateListener listener : list )
{
listener.errorOccured ( t );
}
}
public int getPeriod ()
{
return this.period;
}
public synchronized void addItem ( final String itemId, final DataCallback dataCallback ) throws JIException, AddFailedException
{
if ( this.itemSet.containsKey ( itemId ) )
{
return;
}
this.itemSet.put ( itemId, dataCallback );
if ( isActive () )
{
realizeItem ( itemId );
}
}
public synchronized void removeItem ( final String itemId )
{
if ( !this.itemSet.containsKey ( itemId ) )
{
return;
}
this.itemSet.remove ( itemId );
if ( isActive () )
{
unrealizeItem ( itemId );
}
}
public void connectionStateChanged ( final boolean connected )
{
try
{
if ( connected )
{
start ();
}
else
{
stop ();
}
}
catch ( final Exception e )
{
logger.error ( String.format ( "Failed to change state (%s)", connected ), e );
}
}
protected synchronized void start () throws JIException, IllegalArgumentException, UnknownHostException, NotConnectedException, DuplicateGroupException
{
if ( isActive () )
{
return;
}
logger.debug ( "Create a new group" );
this.group = this.server.addGroup ();
this.group.setActive ( true );
this.active = true;
notifyStateListenersState ( true );
realizeAll ();
}
protected void realizeItem ( final String itemId ) throws JIException, AddFailedException
{
logger.debug ( "Realizing item: {}", itemId );
final DataCallback dataCallback = this.itemSet.get ( itemId );
if ( dataCallback == null )
{
return;
}
final Item item = this.group.addItem ( itemId );
this.items.put ( item, dataCallback );
this.itemMap.put ( itemId, item );
}
protected void unrealizeItem ( final String itemId )
{
final Item item = this.itemMap.remove ( itemId );
this.items.remove ( item );
this.itemCache.remove ( item );
try
{
this.group.removeItem ( itemId );
}
catch ( final Throwable e )
{
logger.error ( String.format ( "Failed to unrealize item '%s'", itemId ), e );
}
}
/*
* FIXME: need some perfomance boost: subscribe all in one call
*/
protected void realizeAll ()
{
for ( final String itemId : this.itemSet.keySet () )
{
try
{
realizeItem ( itemId );
}
catch ( final AddFailedException e )
{
Integer rc = e.getErrors ().get ( itemId );
if ( rc == null )
{
rc = -1;
}
logger.warn ( String.format ( "Failed to add item: %s (%08X)", itemId, rc ) );
}
catch ( final Exception e )
{
logger.warn ( "Failed to realize item: " + itemId, e );
}
}
}
protected void unrealizeAll ()
{
this.items.clear ();
this.itemCache.clear ();
try
{
this.group.clear ();
}
catch ( final JIException e )
{
logger.info ( "Failed to clear group. No problem if we already lost the connection", e );
}
}
protected synchronized void stop () throws JIException
{
if ( !isActive () )
{
return;
}
unrealizeAll ();
this.active = false;
notifyStateListenersState ( false );
try
{
this.group.remove ();
}
catch ( final Throwable t )
{
logger.warn ( "Failed to disable group. No problem if we already lost connection" );
}
this.group = null;
}
public synchronized void clear ()
{
this.itemSet.clear ();
this.items.clear ();
this.itemMap.clear ();
this.itemCache.clear ();
}
protected void updateItem ( final Item item, final ItemState itemState )
{
if ( this.dataLogger != null )
{
this.dataLogger.debug ( "Update item: {}, {}", item.getId (), itemState );
}
final DataCallback dataCallback = this.items.get ( item );
if ( dataCallback == null )
{
return;
}
final ItemState cachedState = this.itemCache.get ( item );
if ( cachedState == null )
{
this.itemCache.put ( item, itemState );
dataCallback.changed ( item, itemState );
}
else
{
if ( !cachedState.equals ( itemState ) )
{
this.itemCache.put ( item, itemState );
dataCallback.changed ( item, itemState );
}
}
}
protected void handleError ( final Throwable e )
{
notifyStateListenersError ( e );
this.server.dispose ();
}
}
@@ -0,0 +1,27 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
public interface AccessStateListener
{
public abstract void stateChanged ( boolean state );
public abstract void errorOccured ( Throwable t );
}
@@ -0,0 +1,61 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import java.util.HashMap;
import java.util.Map;
public class AddFailedException extends Exception
{
/**
*
*/
private static final long serialVersionUID = 5299486640366935298L;
private Map<String, Integer> _errors = new HashMap<String, Integer> ();
private Map<String, Item> _items = new HashMap<String, Item> ();
public AddFailedException ( final Map<String, Integer> errors, final Map<String, Item> items )
{
super ();
this._errors = errors;
this._items = items;
}
/**
* Get the map of item id to error code
* @return the result map containing the failed items
*/
public Map<String, Integer> getErrors ()
{
return this._errors;
}
/**
* Get the map of item it to item object
* @return the result map containing the succeeded items
*/
public Map<String, Item> getItems ()
{
return this._items;
}
}
@@ -0,0 +1,136 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import java.net.UnknownHostException;
import org.jinterop.dcom.common.JIException;
import org.openscada.opc.dcom.common.EventHandler;
import org.openscada.opc.dcom.common.KeyedResult;
import org.openscada.opc.dcom.common.KeyedResultSet;
import org.openscada.opc.dcom.common.ResultSet;
import org.openscada.opc.dcom.da.IOPCDataCallback;
import org.openscada.opc.dcom.da.OPCDATASOURCE;
import org.openscada.opc.dcom.da.ValueData;
import org.openscada.opc.dcom.da.impl.OPCAsyncIO2;
import org.openscada.opc.lib.common.NotConnectedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Async20Access extends AccessBase implements IOPCDataCallback
{
private static Logger logger = LoggerFactory.getLogger ( Async20Access.class );
private EventHandler eventHandler = null;
private boolean initialRefresh = false;
public Async20Access ( final Server server, final int period, final boolean initialRefresh ) throws IllegalArgumentException, UnknownHostException, NotConnectedException, JIException, DuplicateGroupException
{
super ( server, period );
this.initialRefresh = initialRefresh;
}
public Async20Access ( final Server server, final int period, final boolean initialRefresh, final String logTag ) throws IllegalArgumentException, UnknownHostException, NotConnectedException, JIException, DuplicateGroupException
{
super ( server, period, logTag );
this.initialRefresh = initialRefresh;
}
@Override
protected synchronized void start () throws JIException, IllegalArgumentException, UnknownHostException, NotConnectedException, DuplicateGroupException
{
if ( isActive () )
{
return;
}
super.start ();
this.eventHandler = this.group.attach ( this );
if ( !this.items.isEmpty () && this.initialRefresh )
{
final OPCAsyncIO2 async20 = this.group.getAsyncIO20 ();
if ( async20 == null )
{
throw new NotConnectedException ();
}
this.group.getAsyncIO20 ().refresh ( OPCDATASOURCE.OPC_DS_CACHE, 0 );
}
}
@Override
protected synchronized void stop () throws JIException
{
if ( !isActive () )
{
return;
}
if ( this.eventHandler != null )
{
try
{
this.eventHandler.detach ();
}
catch ( final Throwable e )
{
logger.warn ( "Failed to detach group", e );
}
this.eventHandler = null;
}
super.stop ();
}
public void cancelComplete ( final int transactionId, final int serverGroupHandle )
{
}
public void dataChange ( final int transactionId, final int serverGroupHandle, final int masterQuality, final int masterErrorCode, final KeyedResultSet<Integer, ValueData> result )
{
logger.debug ( "dataChange - transId {}, items: {}", transactionId, result.size () );
final Group group = this.group;
if ( group == null )
{
return;
}
for ( final KeyedResult<Integer, ValueData> entry : result )
{
final Item item = group.findItemByClientHandle ( entry.getKey () );
logger.debug ( "Update for '{}'", item.getId () );
updateItem ( item, new ItemState ( entry.getErrorCode (), entry.getValue ().getValue (), entry.getValue ().getTimestamp (), entry.getValue ().getQuality () ) );
}
}
public void readComplete ( final int transactionId, final int serverGroupHandle, final int masterQuality, final int masterErrorCode, final KeyedResultSet<Integer, ValueData> result )
{
logger.debug ( "readComplete - transId {}", transactionId );
}
public void writeComplete ( final int transactionId, final int serverGroupHandle, final int masterErrorCode, final ResultSet<Integer> result )
{
logger.debug ( "writeComplete - transId {}", transactionId );
}
}
@@ -0,0 +1,230 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AutoReconnectController implements ServerConnectionStateListener
{
private static Logger _log = LoggerFactory.getLogger ( AutoReconnectController.class );
private static final int DEFAULT_DELAY = 5 * 1000;
private int _delay;
private final Server _server;
private final Set<AutoReconnectListener> _listeners = new CopyOnWriteArraySet<AutoReconnectListener> ();
private AutoReconnectState _state = AutoReconnectState.DISABLED;
private Thread _connectTask = null;
public AutoReconnectController ( final Server server )
{
this ( server, DEFAULT_DELAY );
}
public AutoReconnectController ( final Server server, final int delay )
{
super ();
setDelay ( delay );
this._server = server;
this._server.addStateListener ( this );
}
public void addListener ( final AutoReconnectListener listener )
{
if ( listener != null )
{
this._listeners.add ( listener );
listener.stateChanged ( this._state );
}
}
public void removeListener ( final AutoReconnectListener listener )
{
this._listeners.remove ( listener );
}
protected void notifyStateChange ( final AutoReconnectState state )
{
this._state = state;
for ( AutoReconnectListener listener : this._listeners )
{
listener.stateChanged ( state );
}
}
public int getDelay ()
{
return this._delay;
}
/**
* Set the reconnect delay. If the delay less than or equal to zero it will be
* the default delay time.
* @param delay The delay to use
*/
public void setDelay ( int delay )
{
if ( delay <= 0 )
{
delay = DEFAULT_DELAY;
}
this._delay = delay;
}
public synchronized void connect ()
{
if ( isRequested () )
{
return;
}
_log.debug ( "Requesting connection" );
notifyStateChange ( AutoReconnectState.DISCONNECTED );
triggerReconnect ( false );
}
public synchronized void disconnect ()
{
if ( !isRequested () )
{
return;
}
_log.debug ( "Un-Requesting connection" );
notifyStateChange ( AutoReconnectState.DISABLED );
this._server.disconnect ();
}
public boolean isRequested ()
{
return this._state != AutoReconnectState.DISABLED;
}
public synchronized void connectionStateChanged ( final boolean connected )
{
_log.debug ( "Connection state changed: " + connected );
if ( !connected )
{
if ( isRequested () )
{
notifyStateChange ( AutoReconnectState.DISCONNECTED );
triggerReconnect ( true );
}
}
else
{
if ( !isRequested () )
{
this._server.disconnect ();
}
else
{
notifyStateChange ( AutoReconnectState.CONNECTED );
}
}
}
private synchronized void triggerReconnect ( final boolean wait )
{
if ( this._connectTask != null )
{
_log.info ( "Connect thread already running" );
return;
}
_log.debug ( "Trigger reconnect" );
this._connectTask = new Thread ( new Runnable () {
public void run ()
{
boolean result = false;
try
{
result = performReconnect ( wait );
}
finally
{
AutoReconnectController.this._connectTask = null;
_log.debug ( String.format ( "performReconnect completed : %s", result ) );
if ( !result )
{
triggerReconnect ( true );
}
}
}
}, "OPCReconnectThread" );
this._connectTask.setDaemon ( true );
this._connectTask.start ();
}
private boolean performReconnect ( final boolean wait )
{
try
{
if ( wait )
{
notifyStateChange ( AutoReconnectState.WAITING );
_log.debug ( String.format ( "Delaying (%s)...", this._delay ) );
Thread.sleep ( this._delay );
}
}
catch ( InterruptedException e )
{
}
if ( !isRequested () )
{
_log.debug ( "Request canceled during delay" );
return true;
}
try
{
_log.debug ( "Connecting to server" );
notifyStateChange ( AutoReconnectState.CONNECTING );
synchronized ( this )
{
this._server.connect ();
return true;
}
// CONNECTED state will be set by server callback
}
catch ( Throwable e )
{
_log.info ( "Re-connect failed", e );
notifyStateChange ( AutoReconnectState.DISCONNECTED );
return false;
}
}
}
@@ -0,0 +1,25 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
public interface AutoReconnectListener
{
public abstract void stateChanged ( AutoReconnectState state );
}
@@ -0,0 +1,51 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
/**
* A state for the auto-reconnect controller
* @author Jens Reimann
*
*/
public enum AutoReconnectState
{
/**
* Auto reconnect is disabled.
*/
DISABLED,
/**
* Auto reconnect is enabled, but the connection is currently not established.
*/
DISCONNECTED,
/**
* Auto reconnect is enabled, the connection is not established and the controller
* is currently waiting the delay until it will reconnect.
*/
WAITING,
/**
* Auto reconnect is enabled, the connection is not established but the controller
* currently tries to establish the connection.
*/
CONNECTING,
/**
* Auto reconnect is enabled and the connection is established.
*/
CONNECTED
}
@@ -0,0 +1,25 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
public interface DataCallback
{
void changed ( Item item, ItemState itemState );
}
@@ -0,0 +1,30 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
public class DuplicateGroupException extends Exception
{
/**
*
*/
private static final long serialVersionUID = 826553520690295478L;
}
@@ -0,0 +1,80 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import java.util.HashMap;
import java.util.Map;
import org.jinterop.dcom.common.JIException;
import org.openscada.opc.dcom.common.impl.OPCCommon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An error message resolver that will lookup the error code using the
* server interface and will cache the result locally.
* @author Jens Reimann
*
*/
public class ErrorMessageResolver
{
private static Logger _log = LoggerFactory.getLogger ( ErrorMessageResolver.class );
private OPCCommon _opcCommon = null;
private final Map<Integer, String> _messageCache = new HashMap<Integer, String> ();
private int _localeId = 0;
public ErrorMessageResolver ( final OPCCommon opcCommon, final int localeId )
{
super ();
this._opcCommon = opcCommon;
this._localeId = localeId;
}
/**
* Get an error message from an error code
* @param errorCode The error code to look up
* @return the error message or <code>null</code> if no message could be looked up
*/
public synchronized String getMessage ( final int errorCode )
{
String message = this._messageCache.get ( Integer.valueOf ( errorCode ) );
if ( message == null )
{
try
{
message = this._opcCommon.getErrorString ( errorCode, this._localeId );
_log.info ( String.format ( "Resolved %08X to '%s'", errorCode, message ) );
}
catch ( JIException e )
{
_log.warn ( String.format ( "Failed to resolve error code for %08X", errorCode ), e );
}
if ( message != null )
{
this._messageCache.put ( errorCode, message );
}
}
return message;
}
}
@@ -0,0 +1,437 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.jinterop.dcom.common.JIException;
import org.openscada.opc.dcom.common.EventHandler;
import org.openscada.opc.dcom.common.KeyedResult;
import org.openscada.opc.dcom.common.KeyedResultSet;
import org.openscada.opc.dcom.common.Result;
import org.openscada.opc.dcom.common.ResultSet;
import org.openscada.opc.dcom.da.IOPCDataCallback;
import org.openscada.opc.dcom.da.OPCDATASOURCE;
import org.openscada.opc.dcom.da.OPCITEMDEF;
import org.openscada.opc.dcom.da.OPCITEMRESULT;
import org.openscada.opc.dcom.da.OPCITEMSTATE;
import org.openscada.opc.dcom.da.impl.OPCAsyncIO2;
import org.openscada.opc.dcom.da.impl.OPCGroupStateMgt;
import org.openscada.opc.dcom.da.impl.OPCItemMgt;
import org.openscada.opc.dcom.da.impl.OPCSyncIO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Group
{
private static Logger _log = LoggerFactory.getLogger ( Group.class );
private static Random _random = new Random ();
private Server _server = null;
private final int _serverHandle;
private OPCGroupStateMgt _group = null;
private OPCItemMgt _items = null;
private OPCSyncIO _syncIO = null;
private final Map<String, Integer> _itemHandleMap = new HashMap<String, Integer> ();
private final Map<Integer, Item> _itemMap = new HashMap<Integer, Item> ();
private final Map<Integer, Item> _itemClientMap = new HashMap<Integer, Item> ();
Group ( final Server server, final int serverHandle, final OPCGroupStateMgt group ) throws IllegalArgumentException, UnknownHostException, JIException
{
_log.debug ( "Creating new group instance with COM group " + group );
this._server = server;
this._serverHandle = serverHandle;
this._group = group;
this._items = group.getItemManagement ();
this._syncIO = group.getSyncIO ();
}
public void setActive ( final boolean state ) throws JIException
{
this._group.setState ( null, state, null, null, null, null );
}
/**
* remove the group from the server
* @throws JIException
*
*/
public void remove () throws JIException
{
this._server.removeGroup ( this, true );
}
public boolean isActive () throws JIException
{
return this._group.getState ().isActive ();
}
/**
* Get the group name from the server
* @return The group name fetched from the server
* @throws JIException
*/
public String getName () throws JIException
{
return this._group.getState ().getName ();
}
/**
* Change the group name
* @param name the new name of the group
* @throws JIException
*/
public void setName ( final String name ) throws JIException
{
this._group.setName ( name );
}
/**
* Add a single item. Actually calls {@link #addItems(String[])} with only
* one paraemter
* @param item The item to add
* @return The added item
* @throws JIException The add operation failed
* @throws AddFailedException The item was not added due to an error
*/
public Item addItem ( final String item ) throws JIException, AddFailedException
{
Map<String, Item> items = addItems ( item );
return items.get ( item );
}
/**
* Validate item ids and get additional information to them.
* <br>
* According to the OPC specification you should first <em>validate</em>
* the items and the <em>add</em> them. The spec also says that when a server
* lets the item pass validation it must also let them pass the add operation.
* @param items The items to validate
* @return A result map of item id to result information (including error code).
* @throws JIException
*/
public synchronized Map<String, Result<OPCITEMRESULT>> validateItems ( final String... items ) throws JIException
{
OPCITEMDEF[] defs = new OPCITEMDEF[items.length];
for ( int i = 0; i < items.length; i++ )
{
defs[i] = new OPCITEMDEF ();
defs[i].setItemID ( items[i] );
}
KeyedResultSet<OPCITEMDEF, OPCITEMRESULT> result = this._items.validate ( defs );
Map<String, Result<OPCITEMRESULT>> resultMap = new HashMap<String, Result<OPCITEMRESULT>> ();
for ( KeyedResult<OPCITEMDEF, OPCITEMRESULT> resultEntry : result )
{
resultMap.put ( resultEntry.getKey ().getItemID (), new Result<OPCITEMRESULT> ( resultEntry.getValue (), resultEntry.getErrorCode () ) );
}
return resultMap;
}
/**
* Add new items to the group
* @param items The items (by string id) to add
* @return A result map of id to item object
* @throws JIException The add operation completely failed. No item was added.
* @throws AddFailedException If one or more item could not be added. Item without error where added.
*/
public synchronized Map<String, Item> addItems ( final String... items ) throws JIException, AddFailedException
{
// Find which items we already have
Map<String, Integer> handles = findItems ( items );
List<Integer> foundItems = new ArrayList<Integer> ( items.length );
List<String> missingItems = new ArrayList<String> ();
// separate missing items from the found ones
for ( Map.Entry<String, Integer> entry : handles.entrySet () )
{
if ( entry.getValue () == null )
{
missingItems.add ( entry.getKey () );
}
else
{
foundItems.add ( entry.getValue () );
}
}
// now fetch missing items from OPC server
Set<Integer> newClientHandles = new HashSet<Integer> ();
OPCITEMDEF[] itemDef = new OPCITEMDEF[missingItems.size ()];
for ( int i = 0; i < missingItems.size (); i++ )
{
OPCITEMDEF def = new OPCITEMDEF ();
def.setItemID ( missingItems.get ( i ) );
def.setActive ( true );
Integer clientHandle;
do
{
clientHandle = _random.nextInt ();
} while ( this._itemClientMap.containsKey ( clientHandle ) || newClientHandles.contains ( clientHandle ) );
newClientHandles.add ( clientHandle );
def.setClientHandle ( clientHandle );
itemDef[i] = def;
}
// check the result and add new items
Map<String, Integer> failedItems = new HashMap<String, Integer> ();
KeyedResultSet<OPCITEMDEF, OPCITEMRESULT> result = this._items.add ( itemDef );
int i = 0;
for ( KeyedResult<OPCITEMDEF, OPCITEMRESULT> entry : result )
{
if ( entry.getErrorCode () == 0 )
{
Item item = new Item ( this, entry.getValue ().getServerHandle (), itemDef[i].getClientHandle (), entry.getKey ().getItemID () );
addItem ( item );
foundItems.add ( item.getServerHandle () );
}
else
{
failedItems.put ( entry.getKey ().getItemID (), entry.getErrorCode () );
}
i++;
}
// if we have failed items then throw an exception with the result
if ( failedItems.size () != 0 )
{
throw new AddFailedException ( failedItems, findItems ( foundItems ) );
}
// simply return the result in case of success
return findItems ( foundItems );
}
private synchronized void addItem ( final Item item )
{
_log.debug ( String.format ( "Adding item: '%s', %d", item.getId (), item.getServerHandle () ) );
this._itemHandleMap.put ( item.getId (), item.getServerHandle () );
this._itemMap.put ( item.getServerHandle (), item );
this._itemClientMap.put ( item.getClientHandle (), item );
}
private synchronized void removeItem ( final Item item )
{
this._itemHandleMap.remove ( item.getId () );
this._itemMap.remove ( item.getServerHandle () );
this._itemClientMap.remove ( item.getClientHandle () );
}
protected Item getItemByOPCItemId ( final String opcItemId )
{
Integer serverHandle = this._itemHandleMap.get ( opcItemId );
if ( serverHandle == null )
{
_log.debug ( String.format ( "Failed to locate item with id '%s'", opcItemId ) );
return null;
}
_log.debug ( String.format ( "Item '%s' has server id '%d'", opcItemId, serverHandle ) );
return this._itemMap.get ( serverHandle );
}
private synchronized Map<String, Integer> findItems ( final String[] items )
{
Map<String, Integer> data = new HashMap<String, Integer> ();
for ( int i = 0; i < items.length; i++ )
{
data.put ( items[i], this._itemHandleMap.get ( items[i] ) );
}
return data;
}
private synchronized Map<String, Item> findItems ( final Collection<Integer> handles )
{
Map<String, Item> itemMap = new HashMap<String, Item> ();
for ( Integer i : handles )
{
Item item = this._itemMap.get ( i );
if ( item != null )
{
itemMap.put ( item.getId (), item );
}
}
return itemMap;
}
protected void checkItems ( final Item[] items )
{
for ( Item item : items )
{
if ( item.getGroup () != this )
{
throw new IllegalArgumentException ( "Item does not belong to this group" );
}
}
}
public void setActive ( final boolean state, final Item... items ) throws JIException
{
checkItems ( items );
Integer[] handles = new Integer[items.length];
for ( int i = 0; i < items.length; i++ )
{
handles[i] = items[i].getServerHandle ();
}
this._items.setActiveState ( state, handles );
}
protected Integer[] getServerHandles ( final Item[] items )
{
checkItems ( items );
Integer[] handles = new Integer[items.length];
for ( int i = 0; i < items.length; i++ )
{
handles[i] = items[i].getServerHandle ();
}
return handles;
}
public synchronized Map<Item, Integer> write ( final WriteRequest... requests ) throws JIException
{
Item[] items = new Item[requests.length];
for ( int i = 0; i < requests.length; i++ )
{
items[i] = requests[i].getItem ();
}
Integer[] handles = getServerHandles ( items );
org.openscada.opc.dcom.da.WriteRequest[] wr = new org.openscada.opc.dcom.da.WriteRequest[items.length];
for ( int i = 0; i < items.length; i++ )
{
wr[i] = new org.openscada.opc.dcom.da.WriteRequest ( handles[i], requests[i].getValue () );
}
ResultSet<org.openscada.opc.dcom.da.WriteRequest> resultSet = this._syncIO.write ( wr );
Map<Item, Integer> result = new HashMap<Item, Integer> ();
for ( int i = 0; i < requests.length; i++ )
{
Result<org.openscada.opc.dcom.da.WriteRequest> entry = resultSet.get ( i );
result.put ( requests[i].getItem (), entry.getErrorCode () );
}
return result;
}
public synchronized Map<Item, ItemState> read ( final boolean device, final Item... items ) throws JIException
{
Integer[] handles = getServerHandles ( items );
KeyedResultSet<Integer, OPCITEMSTATE> states = this._syncIO.read ( device ? OPCDATASOURCE.OPC_DS_DEVICE : OPCDATASOURCE.OPC_DS_CACHE, handles );
Map<Item, ItemState> data = new HashMap<Item, ItemState> ();
for ( KeyedResult<Integer, OPCITEMSTATE> entry : states )
{
Item item = this._itemMap.get ( entry.getKey () );
ItemState state = new ItemState ( entry.getErrorCode (), entry.getValue ().getValue (), entry.getValue ().getTimestamp ().asCalendar (), entry.getValue ().getQuality () );
data.put ( item, state );
}
return data;
}
public Server getServer ()
{
return this._server;
}
public synchronized void clear () throws JIException
{
Integer[] handles = this._itemMap.keySet ().toArray ( new Integer[0] );
try
{
this._items.remove ( handles );
}
finally
{
// in any case clear our maps
this._itemHandleMap.clear ();
this._itemMap.clear ();
this._itemClientMap.clear ();
}
}
public synchronized OPCAsyncIO2 getAsyncIO20 ()
{
return this._group.getAsyncIO2 ();
}
public synchronized EventHandler attach ( final IOPCDataCallback dataCallback ) throws JIException
{
return this._group.attach ( dataCallback );
}
public Item findItemByClientHandle ( final int clientHandle )
{
return this._itemClientMap.get ( clientHandle );
}
public int getServerHandle ()
{
return this._serverHandle;
}
public synchronized void removeItem ( final String opcItemId ) throws IllegalArgumentException, UnknownHostException, JIException
{
_log.debug ( String.format ( "Removing item '%s'", opcItemId ) );
Item item = getItemByOPCItemId ( opcItemId );
if ( item != null )
{
this._group.getItemManagement ().remove ( item.getServerHandle () );
removeItem ( item );
_log.debug ( String.format ( "Removed item '%s'", opcItemId ) );
}
else
{
_log.warn ( String.format ( "Unable to find item '%s'", opcItemId ) );
}
}
}
@@ -0,0 +1,83 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.core.JIVariant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Item
{
private static Logger _log = LoggerFactory.getLogger ( Item.class );
private Group _group = null;
private int _serverHandle = 0;
private int _clientHandle = 0;
private String _id = null;
Item ( final Group group, final int serverHandle, final int clientHandle, final String id )
{
super ();
_log.debug ( String.format ( "Adding new item '%s' (0x%08X) for group %s", id, serverHandle, group.toString () ) );
this._group = group;
this._serverHandle = serverHandle;
this._clientHandle = clientHandle;
this._id = id;
}
public Group getGroup ()
{
return this._group;
}
public int getServerHandle ()
{
return this._serverHandle;
}
public int getClientHandle ()
{
return this._clientHandle;
}
public String getId ()
{
return this._id;
}
public void setActive ( final boolean state ) throws JIException
{
this._group.setActive ( state, this );
}
public ItemState read ( final boolean device ) throws JIException
{
return this._group.read ( device, this ).get ( this );
}
public Integer write ( final JIVariant value ) throws JIException
{
return this._group.write ( new WriteRequest[] { new WriteRequest ( this, value ) } ).get ( this );
}
}
@@ -0,0 +1,163 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import java.util.Calendar;
import org.jinterop.dcom.core.JIVariant;
public class ItemState
{
private int _errorCode = 0;
private JIVariant _value = null;
private Calendar _timestamp = null;
private Short _quality = null;
public ItemState ( final int errorCode, final JIVariant value, final Calendar timestamp, final Short quality )
{
super ();
this._errorCode = errorCode;
this._value = value;
this._timestamp = timestamp;
this._quality = quality;
}
public ItemState ()
{
super ();
}
@Override
public String toString ()
{
return String.format ( "Value: %s, Timestamp: %tc, Quality: %s, ErrorCode: %08x", this._value, this._timestamp, this._quality, this._errorCode );
}
public Short getQuality ()
{
return this._quality;
}
public void setQuality ( final Short quality )
{
this._quality = quality;
}
public Calendar getTimestamp ()
{
return this._timestamp;
}
public void setTimestamp ( final Calendar timestamp )
{
this._timestamp = timestamp;
}
public JIVariant getValue ()
{
return this._value;
}
public void setValue ( final JIVariant value )
{
this._value = value;
}
public int getErrorCode ()
{
return this._errorCode;
}
public void setErrorCode ( final int errorCode )
{
this._errorCode = errorCode;
}
@Override
public int hashCode ()
{
final int PRIME = 31;
int result = 1;
result = PRIME * result + this._errorCode;
result = PRIME * result + ( this._quality == null ? 0 : this._quality.hashCode () );
result = PRIME * result + ( this._timestamp == null ? 0 : this._timestamp.hashCode () );
result = PRIME * result + ( this._value == null ? 0 : this._value.hashCode () );
return result;
}
@Override
public boolean equals ( final Object obj )
{
if ( this == obj )
{
return true;
}
if ( obj == null )
{
return false;
}
if ( getClass () != obj.getClass () )
{
return false;
}
final ItemState other = (ItemState)obj;
if ( this._errorCode != other._errorCode )
{
return false;
}
if ( this._quality == null )
{
if ( other._quality != null )
{
return false;
}
}
else if ( !this._quality.equals ( other._quality ) )
{
return false;
}
if ( this._timestamp == null )
{
if ( other._timestamp != null )
{
return false;
}
}
else if ( !this._timestamp.equals ( other._timestamp ) )
{
return false;
}
if ( this._value == null )
{
if ( other._value != null )
{
return false;
}
}
else if ( !this._value.equals ( other._value ) )
{
return false;
}
return true;
}
}
@@ -0,0 +1,445 @@
/*
* This file is part of the openSCADA project
* Copyright (C) 2006-2011 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* openSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* openSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with openSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledExecutorService;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.core.JIClsid;
import org.jinterop.dcom.core.JIComServer;
import org.jinterop.dcom.core.JIProgId;
import org.jinterop.dcom.core.JISession;
import org.openscada.opc.dcom.da.OPCNAMESPACETYPE;
import org.openscada.opc.dcom.da.OPCSERVERSTATUS;
import org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace;
import org.openscada.opc.dcom.da.impl.OPCGroupStateMgt;
import org.openscada.opc.dcom.da.impl.OPCServer;
import org.openscada.opc.lib.common.AlreadyConnectedException;
import org.openscada.opc.lib.common.ConnectionInformation;
import org.openscada.opc.lib.common.NotConnectedException;
import org.openscada.opc.lib.da.browser.FlatBrowser;
import org.openscada.opc.lib.da.browser.TreeBrowser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Server {
private static Logger logger = LoggerFactory.getLogger(Server.class);
private final ConnectionInformation connectionInformation;
private JISession session;
private JIComServer comServer;
private OPCServer server;
private boolean defaultActive = true;
private int defaultUpdateRate = 1000;
private Integer defaultTimeBias;
private Float defaultPercentDeadband;
private int defaultLocaleID = 0;
private ErrorMessageResolver errorMessageResolver;
private final Map<Integer, Group> groups = new HashMap<Integer, Group>();
private final List<ServerConnectionStateListener> stateListeners = new CopyOnWriteArrayList<ServerConnectionStateListener>();
private final ScheduledExecutorService scheduler;
public Server(final ConnectionInformation connectionInformation,
final ScheduledExecutorService scheduler) {
super();
this.connectionInformation = connectionInformation;
this.scheduler = scheduler;
}
/**
* Gets the scheduler for the server. Note that this scheduler might get
* blocked for a short time if the connection breaks. It should not be used
* for time critical operations.
*
* @return the scheduler for the server
*/
public ScheduledExecutorService getScheduler() {
return this.scheduler;
}
protected synchronized boolean isConnected() {
return this.session != null;
}
public synchronized void connect() throws IllegalArgumentException,
UnknownHostException, JIException, AlreadyConnectedException {
if (isConnected()) {
throw new AlreadyConnectedException();
}
final int socketTimeout = Integer.getInteger("rpc.socketTimeout", 0);
logger.info(String.format("Socket timeout: %s ", socketTimeout));
try {
if (this.connectionInformation.getClsid() != null) {
this.session = JISession.createSession(
this.connectionInformation.getDomain(),
this.connectionInformation.getUser(),
this.connectionInformation.getPassword());
this.session.setGlobalSocketTimeout(socketTimeout);
this.comServer = new JIComServer(
JIClsid.valueOf(this.connectionInformation.getClsid()),
this.connectionInformation.getHost(), this.session);
} else if (this.connectionInformation.getProgId() != null) {
this.session = JISession.createSession(
this.connectionInformation.getDomain(),
this.connectionInformation.getUser(),
this.connectionInformation.getPassword());
this.session.setGlobalSocketTimeout(socketTimeout);
this.comServer = new JIComServer(
JIProgId.valueOf(this.connectionInformation.getProgId()),
this.connectionInformation.getHost(), this.session);
} else {
throw new IllegalArgumentException(
"Neither clsid nor progid is valid!");
}
this.server = new OPCServer(this.comServer.createInstance());
this.errorMessageResolver = new ErrorMessageResolver(
this.server.getCommon(), this.defaultLocaleID);
} catch (final UnknownHostException e) {
logger.info("Unknown host when connecting to server", e);
cleanup();
throw e;
} catch (final JIException e) {
logger.info("Failed to connect to server", e);
cleanup();
throw e;
} catch (final Throwable e) {
logger.warn("Unknown error", e);
cleanup();
throw new RuntimeException(e);
}
notifyConnectionStateChange(true);
}
/**
* cleanup after the connection is closed
*/
protected void cleanup() {
logger.info("Destroying DCOM session...");
final JISession destructSession = this.session;
final Thread destructor = new Thread(new Runnable() {
public void run() {
final long ts = System.currentTimeMillis();
try {
logger.debug("Starting destruction of DCOM session");
JISession.destroySession(destructSession);
logger.info("Destructed DCOM session");
} catch (final Throwable e) {
logger.warn("Failed to destruct DCOM session", e);
} finally {
logger.info(String.format("Session destruction took %s ms",
System.currentTimeMillis() - ts));
}
}
}, "UtgardSessionDestructor");
destructor.setName("OPCSessionDestructor");
destructor.setDaemon(true);
destructor.start();
logger.info("Destroying DCOM session... forked");
this.errorMessageResolver = null;
this.session = null;
this.comServer = null;
this.server = null;
this.groups.clear();
}
/**
* Disconnect the connection if it is connected
*/
public synchronized void disconnect() {
if (!isConnected()) {
return;
}
try {
notifyConnectionStateChange(false);
} catch (final Throwable t) {
}
cleanup();
}
/**
* Dispose the connection in the case of an error
*/
public void dispose() {
disconnect();
}
protected synchronized Group getGroup(final OPCGroupStateMgt groupMgt)
throws JIException, IllegalArgumentException, UnknownHostException {
final Integer serverHandle = groupMgt.getState().getServerHandle();
if (this.groups.containsKey(serverHandle)) {
return this.groups.get(serverHandle);
} else {
final Group group = new Group(this, serverHandle, groupMgt);
this.groups.put(serverHandle, group);
return group;
}
}
/**
* Add a new named group to the server
*
* @param name
* The name of the group to use. Must be unique or
* <code>null</code> so that the server creates a unique name.
* @return The new group
* @throws NotConnectedException
* If the server is not connected using {@link Server#connect()}
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
* @throws DuplicateGroupException
* If a group with this name already exists
*/
public synchronized Group addGroup(final String name)
throws NotConnectedException, IllegalArgumentException,
UnknownHostException, JIException, DuplicateGroupException {
if (!isConnected()) {
throw new NotConnectedException();
}
try {
final OPCGroupStateMgt groupMgt = this.server.addGroup(name,
this.defaultActive, this.defaultUpdateRate, 0,
this.defaultTimeBias, this.defaultPercentDeadband,
this.defaultLocaleID);
return getGroup(groupMgt);
} catch (final JIException e) {
switch (e.getErrorCode()) {
case 0xC004000C:
throw new DuplicateGroupException();
default:
throw e;
}
}
}
/**
* Add a new group and let the server generate a group name
*
* Actually this method only calls {@link Server#addGroup(String)} with
* <code>null</code> as parameter.
*
* @return the new group
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws NotConnectedException
* @throws JIException
* @throws DuplicateGroupException
*/
public Group addGroup() throws IllegalArgumentException,
UnknownHostException, NotConnectedException, JIException,
DuplicateGroupException {
return addGroup(null);
}
/**
* Find a group by its name
*
* @param name
* The name to look for
* @return The group
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
* @throws UnknownGroupException
* If the group was not found
* @throws NotConnectedException
* If the server is not connected
*/
public Group findGroup(final String name) throws IllegalArgumentException,
UnknownHostException, JIException, UnknownGroupException,
NotConnectedException {
if (!isConnected()) {
throw new NotConnectedException();
}
try {
final OPCGroupStateMgt groupMgt = this.server.getGroupByName(name);
return getGroup(groupMgt);
} catch (final JIException e) {
switch (e.getErrorCode()) {
case 0x80070057:
throw new UnknownGroupException(name);
default:
throw e;
}
}
}
public int getDefaultLocaleID() {
return this.defaultLocaleID;
}
public void setDefaultLocaleID(final int defaultLocaleID) {
this.defaultLocaleID = defaultLocaleID;
}
public Float getDefaultPercentDeadband() {
return this.defaultPercentDeadband;
}
public void setDefaultPercentDeadband(final Float defaultPercentDeadband) {
this.defaultPercentDeadband = defaultPercentDeadband;
}
public Integer getDefaultTimeBias() {
return this.defaultTimeBias;
}
public void setDefaultTimeBias(final Integer defaultTimeBias) {
this.defaultTimeBias = defaultTimeBias;
}
public int getDefaultUpdateRate() {
return this.defaultUpdateRate;
}
public void setDefaultUpdateRate(final int defaultUpdateRate) {
this.defaultUpdateRate = defaultUpdateRate;
}
public boolean isDefaultActive() {
return this.defaultActive;
}
public void setDefaultActive(final boolean defaultActive) {
this.defaultActive = defaultActive;
}
/**
* Get the flat browser
*
* @return The flat browser or <code>null</code> if the functionality is not
* supported
*/
public FlatBrowser getFlatBrowser() {
final OPCBrowseServerAddressSpace browser = this.server.getBrowser();
if (browser == null) {
return null;
}
return new FlatBrowser(browser);
}
/**
* Get the tree browser
*
* @return The tree browser or <code>null</code> if the functionality is not
* supported
* @throws JIException
*/
public TreeBrowser getTreeBrowser() throws JIException {
final OPCBrowseServerAddressSpace browser = this.server.getBrowser();
if (browser == null) {
return null;
}
if (browser.queryOrganization() != OPCNAMESPACETYPE.OPC_NS_HIERARCHIAL) {
return null;
}
return new TreeBrowser(browser);
}
public synchronized String getErrorMessage(final int errorCode) {
if (this.errorMessageResolver == null) {
return String.format("Unknown error (%08X)", errorCode);
}
// resolve message
final String message = this.errorMessageResolver.getMessage(errorCode);
// and return if successfull
if (message != null) {
return message;
}
// return default message
return String.format("Unknown error (%08X)", errorCode);
}
public synchronized void addStateListener(
final ServerConnectionStateListener listener) {
this.stateListeners.add(listener);
listener.connectionStateChanged(isConnected());
}
public synchronized void removeStateListener(
final ServerConnectionStateListener listener) {
this.stateListeners.remove(listener);
}
protected void notifyConnectionStateChange(final boolean connected) {
final List<ServerConnectionStateListener> list = new ArrayList<ServerConnectionStateListener>(
this.stateListeners);
for (final ServerConnectionStateListener listener : list) {
listener.connectionStateChanged(connected);
}
}
public OPCSERVERSTATUS getServerState(final int timeout) throws Throwable {
return new ServerStateOperation(this.server).getServerState(timeout);
}
public OPCSERVERSTATUS getServerState() {
try {
return getServerState(2500);
} catch (final Throwable e) {
logger.info("Server connection failed", e);
dispose();
return null;
}
}
public void removeGroup(final Group group, final boolean force)
throws JIException {
if (this.groups.containsKey(group.getServerHandle())) {
this.server.removeGroup(group.getServerHandle(), force);
this.groups.remove(group.getServerHandle());
}
}
}
@@ -0,0 +1,25 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
public interface ServerConnectionStateListener
{
public abstract void connectionStateChanged ( boolean connected );
}
@@ -0,0 +1,27 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import org.openscada.opc.dcom.da.OPCSERVERSTATUS;
public interface ServerStateListener
{
public void stateUpdate ( OPCSERVERSTATUS state );
}
@@ -0,0 +1,123 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import org.openscada.opc.dcom.da.OPCSERVERSTATUS;
import org.openscada.opc.dcom.da.impl.OPCServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A server state operation which can be interruped
* @author Jens Reimann
*
*/
public class ServerStateOperation implements Runnable
{
private static Logger _log = LoggerFactory.getLogger ( ServerStateOperation.class );
public OPCSERVERSTATUS _serverStatus = null;
public OPCServer _server;
public Throwable _error;
public Object _lock = new Object ();
public boolean _running = false;
public ServerStateOperation ( final OPCServer server )
{
super ();
this._server = server;
}
/**
* Perform the operation.
* <p>
* This method will block until either the serve state has been aquired or the
* timeout triggers cancels the call.
* </p>
*/
public void run ()
{
synchronized ( this._lock )
{
this._running = true;
}
try
{
this._serverStatus = this._server.getStatus ();
synchronized ( this._lock )
{
this._running = false;
this._lock.notify ();
}
}
catch ( Throwable e )
{
_log.info ( "Failed to get server state", e );
this._error = e;
this._running = false;
synchronized ( this._lock )
{
this._lock.notify ();
}
}
}
/**
* Get the server state with a timeout.
* @return the server state or <code>null</code> if the server is not set.
* @param timeout the timeout in ms
* @throws Throwable any error that occurred
*/
public OPCSERVERSTATUS getServerState ( final int timeout ) throws Throwable
{
if ( this._server == null )
{
_log.debug ( "No connection to server. Skipping..." );
return null;
}
Thread t = new Thread ( this, "OPCServerStateReader" );
synchronized ( this._lock )
{
t.start ();
this._lock.wait ( timeout );
if ( this._running )
{
_log.warn ( "State operation still running. Interrupting..." );
t.interrupt ();
throw new InterruptedException ( "Interrupted getting server state" );
}
}
if ( this._error != null )
{
_log.warn ( "An error occurred while getting server state", this._error );
throw this._error;
}
return this._serverStatus;
}
}
@@ -0,0 +1,108 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.openscada.opc.dcom.da.OPCSERVERSTATUS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ServerStateReader
{
private static Logger _log = LoggerFactory.getLogger ( ServerStateReader.class );
private Server _server = null;
private ScheduledExecutorService _scheduler = null;
private final List<ServerStateListener> _listeners = new CopyOnWriteArrayList<ServerStateListener> ();
private ScheduledFuture<?> _job = null;
public ServerStateReader ( final Server server )
{
super ();
this._server = server;
this._scheduler = this._server.getScheduler ();
}
/**
* Create a new server state reader. Please note that the scheduler might get
* blocked for a short period of time in case of a connection failure!
* @param server the server to check
* @param scheduler the scheduler to use
*/
public ServerStateReader ( final Server server, final ScheduledExecutorService scheduler )
{
super ();
this._server = server;
this._scheduler = scheduler;
}
public synchronized void start ()
{
if ( this._job != null )
{
return;
}
this._job = this._scheduler.scheduleAtFixedRate ( new Runnable () {
public void run ()
{
once ();
}
}, 1000, 1000, TimeUnit.MILLISECONDS );
}
public synchronized void stop ()
{
this._job.cancel ( false );
this._job = null;
}
protected void once ()
{
_log.debug ( "Reading server state" );
final OPCSERVERSTATUS state = this._server.getServerState ();
for ( final ServerStateListener listener : new ArrayList<ServerStateListener> ( this._listeners ) )
{
listener.stateUpdate ( state );
}
}
public void addListener ( final ServerStateListener listener )
{
this._listeners.add ( listener );
}
public void removeListener ( final ServerStateListener listener )
{
this._listeners.remove ( listener );
}
}
@@ -0,0 +1,120 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import java.net.UnknownHostException;
import java.util.Map;
import org.jinterop.dcom.common.JIException;
import org.openscada.opc.lib.common.NotConnectedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SyncAccess extends AccessBase implements Runnable
{
private static Logger logger = LoggerFactory.getLogger ( SyncAccess.class );
private Thread runner = null;
private Throwable lastError = null;
public SyncAccess ( final Server server, final int period ) throws IllegalArgumentException, UnknownHostException, NotConnectedException, JIException, DuplicateGroupException
{
super ( server, period );
}
public SyncAccess ( final Server server, final int period, final String logTag ) throws IllegalArgumentException, UnknownHostException, NotConnectedException, JIException, DuplicateGroupException
{
super ( server, period, logTag );
}
public void run ()
{
while ( this.active )
{
try
{
runOnce ();
if ( this.lastError != null )
{
this.lastError = null;
handleError ( null );
}
}
catch ( Throwable e )
{
logger.error ( "Sync read failed", e );
handleError ( e );
this.server.disconnect ();
}
try
{
Thread.sleep ( getPeriod () );
}
catch ( InterruptedException e )
{
}
}
}
protected void runOnce () throws JIException
{
if ( !this.active || this.group == null )
{
return;
}
Map<Item, ItemState> result;
// lock only this section since we could get into a deadlock otherwise
// calling updateItem
synchronized ( this )
{
Item[] items = this.items.keySet ().toArray ( new Item[this.items.size ()] );
result = this.group.read ( false, items );
}
for ( Map.Entry<Item, ItemState> entry : result.entrySet () )
{
updateItem ( entry.getKey (), entry.getValue () );
}
}
@Override
protected synchronized void start () throws JIException, IllegalArgumentException, UnknownHostException, NotConnectedException, DuplicateGroupException
{
super.start ();
this.runner = new Thread ( this, "UtgardSyncReader" );
this.runner.setDaemon ( true );
this.runner.start ();
}
@Override
protected synchronized void stop () throws JIException
{
super.stop ();
this.runner = null;
this.items.clear ();
}
}
@@ -0,0 +1,47 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
public class UnknownGroupException extends Exception
{
private String _name = null;
public UnknownGroupException ( final String name )
{
super ();
this._name = name;
}
/**
*
*/
private static final long serialVersionUID = 1771564928794033075L;
public String getName ()
{
return this._name;
}
public void setName ( final String name )
{
this._name = name;
}
}
@@ -0,0 +1,46 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da;
import org.jinterop.dcom.core.JIVariant;
public class WriteRequest
{
private Item _item = null;
private JIVariant _value = null;
public WriteRequest ( final Item item, final JIVariant value )
{
super ();
this._item = item;
this._value = value;
}
public Item getItem ()
{
return this._item;
}
public JIVariant getValue ()
{
return this._value;
}
}
@@ -0,0 +1,38 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da.browser;
public enum Access
{
READ ( 1 ),
WRITE ( 2 );
private int _code = 0;
private Access ( final int code )
{
this._code = code;
}
public int getCode ()
{
return this._code;
}
}
@@ -0,0 +1,124 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da.browser;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.EnumSet;
import org.jinterop.dcom.common.JIException;
import org.openscada.opc.dcom.common.impl.EnumString;
import org.openscada.opc.dcom.da.OPCBROWSETYPE;
import org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A class implementing base browsing
* @author Jens Reimann
*
*/
public class BaseBrowser
{
private static Logger _log = LoggerFactory.getLogger ( BaseBrowser.class );
protected OPCBrowseServerAddressSpace _browser;
/**
* The batch size is the number of entries that will be requested with one call
* from the server. Sometimes too big batch sizes will cause an exception. And
* smaller batch sizes degrade perfomance. The default is set by {@link EnumString#DEFAULT_BATCH_SIZE}
* and can be overridden by the java property <q>openscada.dcom.enum-batch-size</q>.
*/
protected int _batchSize;
public BaseBrowser ( final OPCBrowseServerAddressSpace browser )
{
this ( browser, EnumString.DEFAULT_BATCH_SIZE );
}
public BaseBrowser ( final OPCBrowseServerAddressSpace browser, final int batchSize )
{
super ();
this._browser = browser;
this._batchSize = batchSize;
}
/**
* Set the batch size
* @param batchSize The new batch size
*/
public void setBatchSize ( final int batchSize )
{
this._batchSize = batchSize;
}
/**
* Get the batch size
* @return the current batch size
*/
public int getBatchSize ()
{
return this._batchSize;
}
/**
* Perform the browse operation.
* @param type
* @param filterCriteria
* @param accessMask
* @param variantType
* @return The browse result
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
*/
protected Collection<String> browse ( final OPCBROWSETYPE type, final String filterCriteria, final EnumSet<Access> accessMask, final int variantType ) throws IllegalArgumentException, UnknownHostException, JIException
{
int accessMaskValue = 0;
if ( accessMask.contains ( Access.READ ) )
{
accessMaskValue |= Access.READ.getCode ();
}
if ( accessMask.contains ( Access.WRITE ) )
{
accessMaskValue |= Access.WRITE.getCode ();
}
_log.debug ( "Browsing with a batch size of " + this._batchSize );
return this._browser.browse ( type, filterCriteria, accessMaskValue, variantType ).asCollection ( this._batchSize );
}
/**
* Browse the access paths for one item.
* @param itemId The item ID to look up the access paths
* @return The collection of the access paths
* @throws JIException
* @throws UnknownHostException
* @throws IllegalArgumentException
*/
public Collection<String> getAccessPaths ( final String itemId ) throws IllegalArgumentException, UnknownHostException, JIException
{
return this._browser.browseAccessPaths ( itemId ).asCollection ( this._batchSize );
}
}
@@ -0,0 +1,123 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da.browser;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
public class Branch
{
private Branch _parent = null;
private String _name = null;
private Collection<Branch> _branches = new LinkedList<Branch> ();
private Collection<Leaf> _leaves = new LinkedList<Leaf> ();
/**
* Create a branch to the virtual root folder
*
*/
public Branch ()
{
super ();
}
/**
* Create a branch with a parent branch and a name of this branch.
* @param parent The parent of this branch
* @param name The name of this branch
*/
public Branch ( final Branch parent, final String name )
{
super ();
this._name = name;
this._parent = parent;
}
/**
* Get all branches.
* <br/>
* They must be filled first with a fill method from the {@link TreeBrowser}
* @return The list of branches
*/
public Collection<Branch> getBranches ()
{
return this._branches;
}
public void setBranches ( final Collection<Branch> branches )
{
this._branches = branches;
}
/**
* Get all leaves.
* <br/>
* They must be filled first with a fill method from the {@link TreeBrowser}
* @return The list of leaves
*/
public Collection<Leaf> getLeaves ()
{
return this._leaves;
}
public void setLeaves ( final Collection<Leaf> leaves )
{
this._leaves = leaves;
}
public String getName ()
{
return this._name;
}
public void setName ( final String name )
{
this._name = name;
}
public Branch getParent ()
{
return this._parent;
}
/**
* Get the list of names from the parent up to this branch
* @return The stack of branch names from the parent up this one
*/
public Collection<String> getBranchStack ()
{
LinkedList<String> branches = new LinkedList<String> ();
Branch currentBranch = this;
while ( currentBranch.getParent () != null )
{
branches.add ( currentBranch.getName () );
currentBranch = currentBranch.getParent ();
}
Collections.reverse ( branches );
return branches;
}
}
@@ -0,0 +1,78 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da.browser;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.EnumSet;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.core.JIVariant;
import org.openscada.opc.dcom.da.OPCBROWSETYPE;
import org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace;
/**
* Browse through the flat server namespace
* @author Jens Reimann <jens.reimann@th4-systems.com>
*
*/
public class FlatBrowser extends BaseBrowser
{
public FlatBrowser ( final OPCBrowseServerAddressSpace browser )
{
super ( browser );
}
public FlatBrowser ( final OPCBrowseServerAddressSpace browser, final int batchSize )
{
super ( browser, batchSize );
}
/**
* Perform a flat browse operation
* @param filterCriteria The filter criteria. Use an empty string if you don't need one.
* @param accessMask The access mask. An empty set will search for all.
* @param variantType The variant type. Must be one of the <code>VT_</code> constants of {@link JIVariant}. Use {@link JIVariant#VT_EMPTY} if you want to browse for all.
* @return The list of entries
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
*/
public Collection<String> browse ( final String filterCriteria, final EnumSet<Access> accessMask, final int variantType ) throws IllegalArgumentException, UnknownHostException, JIException
{
return browse ( OPCBROWSETYPE.OPC_FLAT, filterCriteria, accessMask, variantType );
}
public Collection<String> browse ( final String filterCriteria ) throws IllegalArgumentException, UnknownHostException, JIException
{
return browse ( filterCriteria, EnumSet.noneOf ( Access.class ), JIVariant.VT_EMPTY );
}
public Collection<String> browse () throws IllegalArgumentException, UnknownHostException, JIException
{
return browse ( "", EnumSet.noneOf ( Access.class ), JIVariant.VT_EMPTY );
}
public Collection<String> browse ( final EnumSet<Access> accessMask ) throws IllegalArgumentException, UnknownHostException, JIException
{
return browse ( "", accessMask, JIVariant.VT_EMPTY );
}
}
@@ -0,0 +1,68 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da.browser;
public class Leaf
{
private Branch _parent = null;
private String _name = "";
private String _itemId = null;
public Leaf ( final Branch parent, final String name )
{
this._parent = parent;
this._name = name;
}
public Leaf ( final Branch parent, final String name, final String itemId )
{
this._parent = parent;
this._name = name;
this._itemId = itemId;
}
public String getItemId ()
{
return this._itemId;
}
public void setItemId ( final String itemId )
{
this._itemId = itemId;
}
public String getName ()
{
return this._name;
}
public void setName ( final String name )
{
this._name = name;
}
public Branch getParent ()
{
return this._parent;
}
}
@@ -0,0 +1,240 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.da.browser;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.EnumSet;
import java.util.LinkedList;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.core.JIVariant;
import org.openscada.opc.dcom.da.OPCBROWSEDIRECTION;
import org.openscada.opc.dcom.da.OPCBROWSETYPE;
import org.openscada.opc.dcom.da.impl.OPCBrowseServerAddressSpace;
/**
* Browse through the hierarchical server namespace.
* <br/>
* The operations on the address space browser browser are not synchronized
* as is the TreeBrowser itself. The user must take care of preventing
* simultanious access to this instance and the server address space browser.
* @author Jens Reimann <jens.reimann@th4-systems.com>
*
*/
public class TreeBrowser extends BaseBrowser
{
private String _filterCriteria = "";
private EnumSet<Access> _accessMask = EnumSet.noneOf ( Access.class );
private int _variantType = JIVariant.VT_EMPTY;
/**
* Browse for all items without search parameters.
* <br/>
* This will actually call:
* <br/>
* <code>
* TreeBrowser ( browser, "", EnumSet.noneOf ( Access.class ), JIVariant.VT_EMPTY );
* </code>
* @param browser The browser to use for browsing
*/
public TreeBrowser ( final OPCBrowseServerAddressSpace browser )
{
super ( browser );
}
/**
* Browse for items with search parameters.
* @param browser The browser to use
* @param filterCriteria The filter criteria. It is specific to the server you use.
* @param accessMask The access mask (use <code>EnumSet.noneOf ( Access.class )</code> for all)
* @param variantType The variant type (use <code>JIVariant.VT_EMPTY</code> for all)
*/
public TreeBrowser ( final OPCBrowseServerAddressSpace browser, final String filterCriteria, final EnumSet<Access> accessMask, final int variantType )
{
super ( browser );
this._filterCriteria = filterCriteria;
this._accessMask = accessMask;
this._variantType = variantType;
}
/**
* Move the tree browser to the root folder
* @throws JIException
*/
protected void moveToRoot () throws JIException
{
this._browser.changePosition ( null, OPCBROWSEDIRECTION.OPC_BROWSE_TO );
}
/**
* Move the tree browser to a branch
* @param branch The branch to move to
* @throws JIException
*/
protected void moveToBranch ( final Branch branch ) throws JIException
{
Collection<String> branchStack = branch.getBranchStack ();
moveToRoot ();
for ( String branchName : branchStack )
{
this._browser.changePosition ( branchName, OPCBROWSEDIRECTION.OPC_BROWSE_DOWN );
}
}
/**
* Browse the root branch for its sub-branches.
* @return The list of sub branches
* @throws JIException
* @throws IllegalArgumentException
* @throws UnknownHostException
*/
public Branch browseBranches () throws JIException, IllegalArgumentException, UnknownHostException
{
Branch branch = new Branch ();
fillBranches ( branch );
return branch;
}
/**
* Browse the root branch for this leaves.
* @return The list of leaves
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
*/
public Branch browseLeaves () throws IllegalArgumentException, UnknownHostException, JIException
{
Branch branch = new Branch ();
fillLeaves ( branch );
return branch;
}
/**
* Fill the branch list of the provided branch.
* @param branch The branch to fill.
* @throws JIException
* @throws IllegalArgumentException
* @throws UnknownHostException
*/
public void fillBranches ( final Branch branch ) throws JIException, IllegalArgumentException, UnknownHostException
{
moveToBranch ( branch );
browse ( branch, false, true, false );
}
/**
* Fill the leaf list of the provided branch.
* @param branch The branch to fill.
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
*/
public void fillLeaves ( final Branch branch ) throws IllegalArgumentException, UnknownHostException, JIException
{
moveToBranch ( branch );
browse ( branch, true, false, false );
}
/**
* Browse through all levels of the tree browser.
* @return The whole expanded server address space
* @throws JIException
* @throws IllegalArgumentException
* @throws UnknownHostException
*/
public Branch browse () throws JIException, IllegalArgumentException, UnknownHostException
{
Branch branch = new Branch ();
fill ( branch );
return branch;
}
/**
* Fill the leaves and branches of the branch provided branches including
* alls sub-branches.
* @param branch The branch to fill.
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
*/
public void fill ( final Branch branch ) throws IllegalArgumentException, UnknownHostException, JIException
{
moveToBranch ( branch );
browse ( branch, true, true, true );
}
/**
* Fill the branch object with the leaves of this currently selected branch.
* <br/>
* The server object is not located to the branch before browsing!
* @param branch The branch to fill
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
*/
protected void browseLeaves ( final Branch branch ) throws IllegalArgumentException, UnknownHostException, JIException
{
branch.setLeaves ( new LinkedList<Leaf> () );
for ( String item : browse ( OPCBROWSETYPE.OPC_LEAF, this._filterCriteria, this._accessMask, this._variantType ) )
{
Leaf leaf = new Leaf ( branch, item, this._browser.getItemID ( item ) );
branch.getLeaves ().add ( leaf );
}
}
protected void browseBranches ( final Branch branch, final boolean leaves, final boolean descend ) throws IllegalArgumentException, UnknownHostException, JIException
{
branch.setBranches ( new LinkedList<Branch> () );
for ( String item : browse ( OPCBROWSETYPE.OPC_BRANCH, this._filterCriteria, this._accessMask, this._variantType ) )
{
Branch subBranch = new Branch ( branch, item );
// descend only if we should
if ( descend )
{
this._browser.changePosition ( item, OPCBROWSEDIRECTION.OPC_BROWSE_DOWN );
browse ( subBranch, leaves, true, true );
this._browser.changePosition ( null, OPCBROWSEDIRECTION.OPC_BROWSE_UP );
}
branch.getBranches ().add ( subBranch );
}
}
protected void browse ( final Branch branch, final boolean leaves, final boolean branches, final boolean descend ) throws IllegalArgumentException, UnknownHostException, JIException
{
// process leaves
if ( leaves )
{
browseLeaves ( branch );
}
// process branches
if ( branches )
{
browseBranches ( branch, leaves, descend );
}
}
}
@@ -0,0 +1,43 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.list;
public interface Categories
{
/**
* Category of the OPC DA 1.0 Servers
*/
public final static Category OPCDAServer10 = new Category ( org.openscada.opc.dcom.common.Categories.OPCDAServer10 );
/**
* Category of the OPC DA 2.0 Servers
*/
public final static Category OPCDAServer20 = new Category ( org.openscada.opc.dcom.common.Categories.OPCDAServer20 );
/**
* Category of the OPC DA 3.0 Servers
*/
public final static Category OPCDAServer30 = new Category ( org.openscada.opc.dcom.common.Categories.OPCDAServer30 );
/**
* Category of the XML DA 1.0 Servers
*/
public final static Category XMLDAServer10 = new Category ( org.openscada.opc.dcom.common.Categories.XMLDAServer10 );
}
@@ -0,0 +1,76 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.list;
public class Category
{
private String _catId = null;
public Category ( final String catId )
{
this._catId = catId;
}
@Override
public String toString ()
{
return this._catId;
}
@Override
public int hashCode ()
{
final int PRIME = 31;
int result = 1;
result = PRIME * result + ( this._catId == null ? 0 : this._catId.hashCode () );
return result;
}
@Override
public boolean equals ( final Object obj )
{
if ( this == obj )
{
return true;
}
if ( obj == null )
{
return false;
}
if ( getClass () != obj.getClass () )
{
return false;
}
final Category other = (Category)obj;
if ( this._catId == null )
{
if ( other._catId != null )
{
return false;
}
}
else if ( !this._catId.equals ( other._catId ) )
{
return false;
}
return true;
}
}
@@ -0,0 +1,179 @@
/*
* This file is part of the OpenSCADA project
* Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
*
* OpenSCADA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenSCADA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenSCADA. If not, see
* <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
*/
package org.openscada.opc.lib.list;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.core.JIClsid;
import org.jinterop.dcom.core.JIComServer;
import org.jinterop.dcom.core.JISession;
import org.openscada.opc.dcom.list.ClassDetails;
import org.openscada.opc.dcom.list.Constants;
import org.openscada.opc.dcom.list.impl.OPCServerList;
import rpc.core.UUID;
/**
* A wrapper around the {@link OPCServerList} class which makes the handling somewhat easier.
* @author Jens Reimann &lt;jens.reimann@th4-systems.com&gt;
* @since 0.1.8
*
*/
public class ServerList
{
private final JISession _session;
private final OPCServerList _serverList;
/**
* Create a new instance with an already existing session
* @param session the DCOM session
* @param host the host to query
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
*/
public ServerList ( final JISession session, final String host ) throws IllegalArgumentException, UnknownHostException, JIException
{
this._session = session;
JIComServer comServer = new JIComServer ( JIClsid.valueOf ( Constants.OPCServerList_CLSID ), host, this._session );
this._serverList = new OPCServerList ( comServer.createInstance () );
}
/**
* Create a new instance and a new DCOM session
* @param host the host to contact
* @param user the user to use for authentication
* @param password the password to use for authentication
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
*/
public ServerList ( final String host, final String user, final String password ) throws IllegalArgumentException, UnknownHostException, JIException
{
this ( host, user, password, null );
}
/**
* Create a new instance and a new DCOM session
* @param host the host to contact
* @param user the user to use for authentication
* @param password the password to use for authentication
* @param domain The domain to use for authentication
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
*/
public ServerList ( final String host, final String user, final String password, final String domain ) throws IllegalArgumentException, UnknownHostException, JIException
{
this ( JISession.createSession ( domain, user, password ), host );
}
/**
* Get the details of a opc class
* @param clsId the class to request details for
* @return The class details
* @throws JIException
*/
public ClassDetails getDetails ( final String clsId ) throws JIException
{
return this._serverList.getClassDetails ( JIClsid.valueOf ( clsId ) );
}
/**
* Fetch the class id of a prog id
* @param progId The prog id to look up
* @return the class id or <code>null</code> if none could be found.
* @throws JIException
*/
public String getClsIdFromProgId ( final String progId ) throws JIException
{
JIClsid cls = this._serverList.getCLSIDFromProgID ( progId );
if ( cls == null )
{
return null;
}
return cls.getCLSID ();
}
/**
* List all servers that match the requirements
* @param implemented All implemented categories
* @param required All required categories
* @return A collection of <q>class ids</q>
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
*/
public Collection<String> listServers ( final Category[] implemented, final Category[] required ) throws IllegalArgumentException, UnknownHostException, JIException
{
// convert the type safe categories to plain UUIDs
UUID[] u1 = new UUID[implemented.length];
UUID[] u2 = new UUID[required.length];
for ( int i = 0; i < implemented.length; i++ )
{
u1[i] = new UUID ( implemented[i].toString () );
}
for ( int i = 0; i < required.length; i++ )
{
u2[i] = new UUID ( required[i].toString () );
}
// get them as UUIDs
Collection<UUID> resultU = this._serverList.enumClassesOfCategories ( u1, u2 ).asCollection ();
// and convert to easier usable strings
Collection<String> result = new ArrayList<String> ( resultU.size () );
for ( UUID uuid : resultU )
{
result.add ( uuid.toString () );
}
return result;
}
/**
* List all servers that match the requirements and return the class details
* @param implemented All implemented categories
* @param required All required categories
* @return a collection of matching server and their class information
* @throws IllegalArgumentException
* @throws UnknownHostException
* @throws JIException
*/
public Collection<ClassDetails> listServersWithDetails ( final Category[] implemented, final Category[] required ) throws IllegalArgumentException, UnknownHostException, JIException
{
Collection<String> resultString = listServers ( implemented, required );
List<ClassDetails> result = new ArrayList<ClassDetails> ( resultString.size () );
for ( String clsId : resultString )
{
result.add ( getDetails ( clsId ) );
}
return result;
}
}