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
@@ -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;
}
}