diff --git a/TitaniumAS.Opc.Client.Tests/TitaniumAS.Opc.Client.Tests.csproj b/TitaniumAS.Opc.Client.Tests/TitaniumAS.Opc.Client.Tests.csproj
index f795428..9b9b6cc 100644
--- a/TitaniumAS.Opc.Client.Tests/TitaniumAS.Opc.Client.Tests.csproj
+++ b/TitaniumAS.Opc.Client.Tests/TitaniumAS.Opc.Client.Tests.csproj
@@ -8,7 +8,7 @@
PropertiesTitaniumAS.Opc.Client.TestsTitaniumAS.Opc.Client.Tests
- v4.5
+ v4.8512{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}10.0
diff --git a/TitaniumAS.Opc.Client.Tests/app.config b/TitaniumAS.Opc.Client.Tests/app.config
index d61b593..6af320a 100644
--- a/TitaniumAS.Opc.Client.Tests/app.config
+++ b/TitaniumAS.Opc.Client.Tests/app.config
@@ -8,4 +8,4 @@
-
+
diff --git a/TitaniumAS.Opc.Client/Da/OpcDaServer.cs b/TitaniumAS.Opc.Client/Da/OpcDaServer.cs
index 1adbeeb..cd035e8 100644
--- a/TitaniumAS.Opc.Client/Da/OpcDaServer.cs
+++ b/TitaniumAS.Opc.Client/Da/OpcDaServer.cs
@@ -19,592 +19,603 @@
namespace TitaniumAS.Opc.Client.Da
{
+ ///
+ /// Represents the OPC DA server.
+ ///
+ ///
+ public class OpcDaServer : IOpcDaServer
+ {
+ private static readonly ILog Log = LogManager.GetLogger();
+ private readonly List _groups = new List();
+ private readonly ConnectionPoint _shutdownConnectionPoint;
+ private string _clientName;
+ private bool _disposed;
+ private OpcServerDescription _serverDescription;
+
///
- /// Represents the OPC DA server.
+ /// Initializes a new instance of the class.
///
- ///
- public class OpcDaServer : IOpcDaServer
- {
- private static readonly ILog Log = LogManager.GetLogger();
- private readonly List _groups = new List();
- private readonly ConnectionPoint _shutdownConnectionPoint;
- private string _clientName;
- private bool _disposed;
- private OpcServerDescription _serverDescription;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The server URI.
- public OpcDaServer(Uri uri)
- {
- UrlValidator.CheckOpcUrl(uri);
+ /// The server URI.
+ public OpcDaServer(Uri uri, ComProxyBlanket comProxyBlanket = null)
+ {
+ UrlValidator.CheckOpcUrl(uri);
- Uri = uri;
- ComProxyBlanket = ComProxyBlanket.Default;
+ Uri = uri;
+ ComProxyBlanket = comProxyBlanket ?? ComProxyBlanket.Default;
- var shutdown = new OpcShutdown();
- shutdown.Shutdown += OnShutdown;
- ComWrapper.RpcFailed += OnRpcFailed;
+ var shutdown = new OpcShutdown();
+ shutdown.Shutdown += OnShutdown;
+ ComWrapper.RpcFailed += OnRpcFailed;
- _shutdownConnectionPoint = new ConnectionPoint(shutdown);
- _clientName = Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName);
- }
+ _shutdownConnectionPoint = new ConnectionPoint(shutdown);
+ _clientName = Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName);
+ }
- ///
- /// Initializes a new instance of the class.
- ///
- /// The OPC server programmatic identifier or class identifier.
- /// The OPC server host.
- public OpcDaServer(string progIdOrClsid, string host = null)
- : this(UrlBuilder.Build(progIdOrClsid, host))
- {
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The OPC server programmatic identifier or class identifier.
+ /// The OPC server host.
+ public OpcDaServer(string progIdOrClsid, string host = null, ComProxyBlanket comProxyBlanket = null)
+ : this(UrlBuilder.Build(progIdOrClsid, host), comProxyBlanket)
+ {
+ }
- ///
- /// Initializes a new instance of the class.
- ///
- /// The OPC server class identifier.
- /// The OPC server host.
- public OpcDaServer(Guid clsid, string host = null)
- : this(UrlBuilder.Build(clsid, host))
- {
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The OPC server class identifier.
+ /// The OPC server host.
+ public OpcDaServer(Guid clsid, string host = null, ComProxyBlanket comProxyBlanket = null)
+ : this(UrlBuilder.Build(clsid, host), comProxyBlanket)
+ {
+ }
- ///
- /// Gets the COM proxy blanket of this instance.
- ///
- ///
- /// The COM proxy blanket.
- ///
- public ComProxyBlanket ComProxyBlanket { get; set; }
-
- ///
- /// Gets the actual COM object.
- ///
- ///
- /// The actual COM object.
- ///
- public object ComObject { get; private set; }
-
- ///
- /// Gets the server description.
- ///
- ///
- /// The server description.
- ///
- public OpcServerDescription ServerDescription
- {
- get
- {
- if (_serverDescription == null)
- {
- var enumerator = new OpcServerEnumeratorAuto();
- _serverDescription = UrlParser.Parse(
- Uri,
- (host, progId) => enumerator.GetServerDescription(host, progId),
- (host, clsid) => enumerator.GetServerDescription(host, clsid));
- }
- return _serverDescription;
- }
- }
+ ///
+ /// Gets the COM proxy blanket of this instance.
+ ///
+ ///
+ /// The COM proxy blanket.
+ ///
+ public ComProxyBlanket ComProxyBlanket { get; set; }
- ///
- /// Connects the server instance to COM server.
- ///
- /// Already connected to the OPC DA server.
- public void Connect()
- {
- if (IsConnected)
- throw new InvalidOperationException("Already connected to the OPC DA server.");
-
- Log.TraceFormat("Connecting to '{0}' opc server", Uri);
-
- var enumerator = new OpcServerEnumeratorAuto();
- Tuple hostAndCLSID = UrlParser.Parse(
- Uri,
- (host, progId) => new Tuple(host, enumerator.CLSIDFromProgId(progId, host)),
- (host, clsid) => new Tuple(host, clsid));
-
- ComObject = Com.CreateInstanceWithBlanket(hostAndCLSID.Item2, hostAndCLSID.Item1, null, ComProxyBlanket);
- _shutdownConnectionPoint.TryConnect(ComObject);
-
- Log.TraceFormat("Connected to '{0}' opc server.", Uri);
- try
- {
- ClientName = _clientName;
- }
- catch (Exception ex)
- {
- Log.Warn("Cannot setup name of client.", ex);
- }
- OnConnectionStateChanged(true);
- }
+ ///
+ /// Gets the actual COM object.
+ ///
+ ///
+ /// The actual COM object.
+ ///
+ public object ComObject { get; private set; }
- ///
- /// Asynchronout connect method
- ///
- ///
- /// A running task to wait on for connect
- ///
- ///
- public Task ConnectAsync()
- {
- return Task.Factory.StartNew(() => { Connect(); });
- }
+ ///
+ /// Gets the server description.
+ ///
+ ///
+ /// The server description.
+ ///
+ public OpcServerDescription ServerDescription
+ {
+ get
+ {
+ if (_serverDescription == null)
+ {
+ var enumerator = new OpcServerEnumeratorAuto();
+ _serverDescription = UrlParser.Parse(
+ Uri,
+ (host, progId) => enumerator.GetServerDescription(host, progId),
+ (host, clsid) => enumerator.GetServerDescription(host, clsid));
+ }
+ return _serverDescription;
+ }
+ }
- private void DisconnectImpl(bool rpcFailed = false)
- {
- if (!IsConnected)
- return;
-
- Log.TraceFormat("Disconnecting from '{0}' opc server", Uri);
- if (!rpcFailed)
- {
- _shutdownConnectionPoint.Disconnect();
- }
- RemoveAllGroups(rpcFailed);
-
- if (ComObject != null)
- {
- try
- {
- ComObject.ReleaseComServer();
- }
- catch (Exception ex)
- {
- Log.Error("Failed to release opc server's COM object", ex);
- }
- }
-
- ComObject = null;
- Log.TraceFormat("Disconnected from '{0}' opc server", Uri);
- OnConnectionStateChanged(false);
- }
+ ///
+ /// Connects the server instance to COM server.
+ ///
+ /// Already connected to the OPC DA server.
+ public void Connect()
+ {
+ if (IsConnected)
+ throw new InvalidOperationException("Already connected to the OPC DA server.");
+
+ Log.TraceFormat("Connecting to '{0}' opc server", Uri);
+
+ var enumerator = new OpcServerEnumeratorAuto();
+ Tuple hostAndCLSID = UrlParser.Parse(
+ Uri,
+ (host, progId) => new Tuple(host, enumerator.CLSIDFromProgId(progId, host)),
+ (host, clsid) => new Tuple(host, clsid));
+
+ ComObject = Com.CreateInstanceWithBlanket(hostAndCLSID.Item2, hostAndCLSID.Item1, null, ComProxyBlanket);
+ _shutdownConnectionPoint.TryConnect(ComObject);
+
+ Log.TraceFormat("Connected to '{0}' opc server.", Uri);
+ try
+ {
+ ClientName = _clientName;
+ }
+ catch (Exception ex)
+ {
+ Log.Warn("Cannot setup name of client.", ex);
+ }
+ OnConnectionStateChanged(true);
+ }
+ ///
+ /// Asynchronout connect method
+ ///
+ ///
+ /// A running task to wait on for connect
+ ///
+ ///
+ public Task ConnectAsync()
+ {
+ return Task.Factory.StartNew(() => { Connect(); });
+ }
- ///
- /// Releases connection to COM server for the server instance.
- ///
- public void Disconnect()
- {
- DisconnectImpl();
- }
+ private void DisconnectImpl(bool rpcFailed = false)
+ {
+ if (!IsConnected)
+ return;
- ///
- /// Gets the server URI. The URL follows a template: opcda://host/progIdOrCLSID.
- ///
- ///
- /// The server URI.
- ///
- public Uri Uri { get; private set; }
-
- ///
- /// Gets a value indicating whether the server instance is connected to COM server.
- ///
- ///
- /// true if the server instance is connected to COM server; otherwise, false.
- ///
- public bool IsConnected
- {
- get { return ComObject != null; }
- }
+ Log.TraceFormat("Disconnecting from '{0}' opc server", Uri);
+ if (!rpcFailed)
+ {
+ _shutdownConnectionPoint.Disconnect();
+ }
+ RemoveAllGroups(rpcFailed);
- ///
- /// Gets or sets the current culture for the server.
- ///
- ///
- /// The current culture for the server.
- ///
- public CultureInfo Culture
+ if (ComObject != null)
+ {
+ try
{
- get
- {
- CheckConnected();
- int localeId = As().LocaleID;
- return CultureHelper.GetCultureInfo(localeId);
- }
- set
- {
- CheckConnected();
- As().LocaleID = CultureHelper.GetLocaleId(value);
- }
+ ComObject.ReleaseComServer();
}
-
- ///
- /// Queries available cultures of the server.
- ///
- ///
- /// Array of available cultures.
- ///
- public CultureInfo[] QueryAvailableCultures()
+ catch (Exception ex)
{
- CheckConnected();
- return As().QueryAvailableLocaleIDs().Select(CultureHelper.GetCultureInfo).ToArray();
+ Log.Error("Failed to release opc server's COM object", ex);
}
+ }
- ///
- /// Gets or sets the client name.
- ///
- ///
- /// The client name.
- ///
- public string ClientName
- {
- get { return _clientName; }
- set
- {
- _clientName = value;
- if (IsConnected)
- {
- As().ClientName = value;
- }
- }
- }
+ ComObject = null;
+ Log.TraceFormat("Disconnected from '{0}' opc server", Uri);
+ OnConnectionStateChanged(false);
+ }
- ///
- /// Gets the groups of the server.
- ///
- ///
- /// The server groups.
- ///
- public ReadOnlyCollection Groups
- {
- get { return _groups.AsReadOnly(); }
- }
+ ///
+ /// Releases connection to COM server for the server instance.
+ ///
+ public void Disconnect()
+ {
+ DisconnectImpl();
+ }
- ///
- /// Gets or attachs arbitrary user data to the server.
- ///
- ///
- /// The user data.
- ///
- public object UserData { get; set; }
-
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- ///
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
+ ///
+ /// Gets the server URI. The URL follows a template: opcda://host/progIdOrCLSID.
+ ///
+ ///
+ /// The server URI.
+ ///
+ public Uri Uri { get; private set; }
- ///
- /// Occurs when OPC server shutdowns right after connection to COM server has releases.
- ///
- public event EventHandler Shutdown;
-
- ///
- /// Occurs when the server either connects or releases connection to COM server.
- ///
- public event EventHandler ConnectionStateChanged;
-
- ///
- /// Occurs when the server groups have changed.
- ///
- public event EventHandler GroupsChanged;
-
- ///
- /// Removes the group from the server.
- ///
- /// The server group.
- /// The group doesn't belong to the server;group
- public void RemoveGroup(OpcDaGroup group)
- {
- CheckConnected();
+ ///
+ /// Gets a value indicating whether the server instance is connected to COM server.
+ ///
+ ///
+ /// true if the server instance is connected to COM server; otherwise, false.
+ ///
+ public bool IsConnected
+ {
+ get { return ComObject != null; }
+ }
- if (group.Server != this)
- throw new ArgumentException("The group doesn't belong to the server", "group");
- TryRemoveGroup(@group);
- }
+ ///
+ /// Gets or sets the current culture for the server.
+ ///
+ ///
+ /// The current culture for the server.
+ ///
+ public CultureInfo Culture
+ {
+ get
+ {
+ CheckConnected();
+ int localeId = As().LocaleID;
+ return CultureHelper.GetCultureInfo(localeId);
+ }
+ set
+ {
+ CheckConnected();
+ As().LocaleID = CultureHelper.GetLocaleId(value);
+ }
+ }
- ///
- /// Gets the server status.
- ///
- ///
- /// The server status.
- ///
- public OpcDaServerStatus GetStatus()
- {
- CheckConnected();
- return new OpcDaServerStatus(As().GetStatus());
- }
+ ///
+ /// Queries available cultures of the server.
+ ///
+ ///
+ /// Array of available cultures.
+ ///
+ public CultureInfo[] QueryAvailableCultures()
+ {
+ CheckConnected();
+ return As().QueryAvailableLocaleIDs().Select(CultureHelper.GetCultureInfo).ToArray();
+ }
- ///
- /// Reads one or more values, qualities and timestamps for the items specified using MaxAge. If the information in the
- /// cache is within the MaxAge, then the data will be obtained from the cache, otherwise the server must access the
- /// device for the requested information.
- ///
- /// The server item identifiers.
- /// The list of MaxAges for the server items.
- ///
- /// The values of the server items.
- ///
- public OpcDaVQTE[] Read(IList itemIds, IList maxAge)
+ ///
+ /// Gets or sets the client name.
+ ///
+ ///
+ /// The client name.
+ ///
+ public string ClientName
+ {
+ get { return _clientName; }
+ set
+ {
+ _clientName = value;
+ if (IsConnected)
{
- CheckConnected();
- return As().Read(itemIds, maxAge);
+ As().ClientName = value;
}
+ }
+ }
- ///
- /// Writes one or more values, qualities and timestamps for the items specified. If a client attempts to write VQ, VT,
- /// or VQT it should expect that the server will write them all or none at all.
- ///
- /// The server item identifiers.
- /// The list of values, qualities and timestamps.
- ///
- /// Array of HRESULTs indicating the success of the individual item Writes.
- ///
- public HRESULT[] WriteVQT(IList itemIds, IList values)
- {
- CheckConnected();
- return As().WriteVQT(itemIds, values);
- }
+ ///
+ /// Gets the groups of the server.
+ ///
+ ///
+ /// The server groups.
+ ///
+ public ReadOnlyCollection Groups
+ {
+ get { return _groups.AsReadOnly(); }
+ }
- ///
- /// Adds the server group by group name and group state (optional).
- ///
- /// The group name.
- /// The group state.
- ///
- /// Added group.
- ///
- public OpcDaGroup AddGroup(string name, OpcDaGroupState state = null)
- {
- CheckConnected();
-
- if (state == null)
- state = new OpcDaGroupState();
-
- int serverGroupHandle;
- TimeSpan revisedUpdateRate;
- int localeId = CultureHelper.GetLocaleId(state.Culture);
-
- object opcDaGroup = As().AddGroup(name,
- state.IsActive.GetValueOrDefault(false),
- state.UpdateRate.GetValueOrDefault(TimeSpan.FromSeconds(1)),
- state.ClientHandle.GetValueOrDefault(0),
- state.TimeBias,
- state.PercentDeadband,
- localeId, out serverGroupHandle, out revisedUpdateRate);
-
- OpcDaGroup @group = CreateGroupWrapper(opcDaGroup);
- @group.UserData = state.UserData;
- OnGroupsChanged(new OpcDaServerGroupsChangedEventArgs(group, null));
- return @group;
- }
+ ///
+ /// Gets or attachs arbitrary user data to the server.
+ ///
+ ///
+ /// The user data.
+ ///
+ public object UserData { get; set; }
- ///
- /// Synchronizes the server groups. It means existing groups will be replaced with new groups. It fires GroupsChanged
- /// event after synchronization.
- ///
- public void SyncGroups()
- {
- CheckConnected();
- List newGroups = EnumerateGroups();
-
- // Dispose old groups
- foreach (OpcDaGroup @group in _groups)
- {
- ((IDisposable) @group).Dispose();
- OnGroupsChanged(new OpcDaServerGroupsChangedEventArgs(null, @group));
- }
- _groups.Clear();
-
- // Add new groups
- _groups.InsertRange(0, newGroups);
- foreach (OpcDaGroup @group in _groups)
- {
- OnGroupsChanged(new OpcDaServerGroupsChangedEventArgs(@group, null));
- }
- }
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
- private void OnRpcFailed(object sender, RpcFailedEventArgs args)
- {
- if (args.UserData != this) return;
- try
- {
- DisconnectImpl(true);
- }
- catch (Exception ex)
- {
- Log.Error("Disconnect failed.",ex);
- }
- try
- {
- OnShutdown(new OpcShutdownEventArgs("RPC failed", args.Error));
- }
- catch (Exception ex)
- {
- Log.ErrorFormat("Error during shutdown of '{0}' opc server.", ex, Uri);
- }
- }
+ ///
+ /// Occurs when OPC server shutdowns right after connection to COM server has releases.
+ ///
+ public event EventHandler Shutdown;
- private void OnShutdown(object sender, OpcShutdownEventArgs args)
- {
- try
- {
- DisconnectImpl();
- }
- catch (Exception ex)
- {
- Log.Error("Disconnect failed.", ex);
- }
-
- try
- {
- OnShutdown(new OpcShutdownEventArgs(args.Reason, args.Error));
- }
- catch (Exception ex)
- {
- Log.ErrorFormat("Error during shutdown of '{0}' opc server.", ex, Uri);
- }
- }
+ ///
+ /// Occurs when the server either connects or releases connection to COM server.
+ ///
+ public event EventHandler ConnectionStateChanged;
- ///
- /// Try to cast this instance to specified COM wrapper type.
- ///
- /// The COM wrapper type.
- /// The wrapped instance or null.
- public T As() where T : ComWrapper
- {
- try
- {
- if (ComObject == null)
- return default(T);
- return (T) Activator.CreateInstance(typeof (T), ComObject, this);
- }
- catch
- {
- return default(T);
- }
- }
+ ///
+ /// Occurs when the server groups have changed.
+ ///
+ public event EventHandler GroupsChanged;
- ///
- /// Determines whether this instance is of specified COM wrapper type.
- ///
- /// The COM wrapper type.
- /// true if this instance is of specified COM wrapper type; otherwise, false.
- public bool Is() where T : ComWrapper
- {
- return As() != null;
- }
+ ///
+ /// Removes the group from the server.
+ ///
+ /// The server group.
+ /// The group doesn't belong to the server;group
+ public void RemoveGroup(OpcDaGroup group)
+ {
+ CheckConnected();
- private void RemoveAllGroups(bool rpcFailed = false)
- {
- foreach (OpcDaGroup opcDaGroup in _groups.ToArray())
- {
- TryRemoveGroup(opcDaGroup,rpcFailed);
- }
- _groups.Clear();
- }
+ if (group.Server != this)
+ throw new ArgumentException("The group doesn't belong to the server", "group");
+ TryRemoveGroup(@group);
+ }
- ///
- /// Returns a that represents this instance.
- ///
- ///
- /// A that represents this instance.
- ///
- public override string ToString()
- {
- return Uri.ToString();
- }
+ ///
+ /// Gets the server status.
+ ///
+ ///
+ /// The server status.
+ ///
+ public OpcDaServerStatus GetStatus()
+ {
+ CheckConnected();
+ return new OpcDaServerStatus(As().GetStatus());
+ }
- private void CheckConnected()
- {
- if (!IsConnected)
- throw ExceptionHelper.NotConnected();
- }
+ ///
+ /// Reads one or more values, qualities and timestamps for the items specified using MaxAge. If the information in the
+ /// cache is within the MaxAge, then the data will be obtained from the cache, otherwise the server must access the
+ /// device for the requested information.
+ ///
+ /// The server item identifiers.
+ /// The list of MaxAges for the server items.
+ ///
+ /// The values of the server items.
+ ///
+ public OpcDaVQTE[] Read(IList itemIds, IList maxAge)
+ {
+ CheckConnected();
+ return As().Read(itemIds, maxAge);
+ }
- private List EnumerateGroupNames(OpcDaEnumScope scope = OpcDaEnumScope.All)
- {
- object enumeratorObj = As().CreateGroupEnumerator((int) scope);
- var enumerator = (IEnumString) enumeratorObj;
- return enumerator.EnumareateAllAndRelease(OpcConfiguration.BatchSize);
- }
+ ///
+ /// Writes one or more values, qualities and timestamps for the items specified. If a client attempts to write VQ, VT,
+ /// or VQT it should expect that the server will write them all or none at all.
+ ///
+ /// The server item identifiers.
+ /// The list of values, qualities and timestamps.
+ ///
+ /// Array of HRESULTs indicating the success of the individual item Writes.
+ ///
+ public HRESULT[] WriteVQT(IList itemIds, IList values)
+ {
+ CheckConnected();
+ return As().WriteVQT(itemIds, values);
+ }
- private List EnumerateGroups(OpcDaEnumScope scope = OpcDaEnumScope.All)
- {
- object enumeratorObj = As().CreateGroupEnumerator((int) scope);
- var enumerator = (IEnumUnknown) enumeratorObj;
- List