#region Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
//-----------------------------------------------------------------------------
// Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
// Web: https://www.technosoftware.com
//
// The source code in this file is covered under a dual-license scenario:
// - Owner of a purchased license: SCLA 1.0
// - GPL V3: everybody else
//
// SCLA license terms accompanied with this source code.
// See SCLA 1.0: https://technosoftware.com/license/Source_Code_License_Agreement.pdf
//
// GNU General Public License as published by the Free Software Foundation;
// version 3 of the License are accompanied with this source code.
// See https://technosoftware.com/license/GPLv3License.txt
//
// This source code 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.
//-----------------------------------------------------------------------------
#endregion Copyright (c) 2011-2023 Technosoftware GmbH. All rights reserved
#region Using Directives
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
#endregion
namespace Technosoftware.DaAeHdaClient.Da
{
///
/// This class is the main interface to access an OPC Data Access server.
///
[Serializable]
public class TsCDaServer : OpcServer, ITsDaServer
{
#region Names Class
/// A set of names for fields used in serialization.
private class Names
{
internal const string Filters = "Filters";
internal const string Subscriptions = "Subscription";
}
#endregion
#region Fields
///
/// A list of subscriptions for the server.
///
private TsCDaSubscriptionCollection subscriptions_ = new TsCDaSubscriptionCollection();
///
/// The local copy of the result filters.
///
private int filters_ = (int)TsCDaResultFilter.All | (int)TsCDaResultFilter.ClientHandle;
#endregion
#region Constructors, Destructor, Initialization
///
/// Initializes the object.
///
public TsCDaServer()
{
}
///
/// Initializes the object with a factory and a default OpcUrl.
///
/// The OpcFactory used to connect to remote servers.
/// The network address of a remote server.
public TsCDaServer(OpcFactory factory, OpcUrl url)
:
base(factory, url)
{
}
///
/// Constructs a server by de-serializing its OpcUrl from the stream.
///
protected TsCDaServer(SerializationInfo info, StreamingContext context)
:
base(info, context)
{
filters_ = (int)info.GetValue(Names.Filters, typeof(int));
var subscriptions = (TsCDaSubscription[])info.GetValue(Names.Subscriptions, typeof(TsCDaSubscription[]));
if (subscriptions != null)
{
Array.ForEach(subscriptions, subscription => subscriptions_.Add(subscription));
}
}
#endregion
#region Properties
///
/// Returns an array of all subscriptions for the server.
///
public TsCDaSubscriptionCollection Subscriptions => subscriptions_;
///
/// The current result filters applied by the server.
///
public int Filters => filters_;
#endregion
#region Class properties serialization helpers
/// Serializes a server into a stream.
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue(Names.Filters, filters_);
TsCDaSubscription[] subscriptions = null;
if (subscriptions_.Count > 0)
{
subscriptions = new TsCDaSubscription[subscriptions_.Count];
for (var ii = 0; ii < subscriptions.Length; ii++)
{
subscriptions[ii] = subscriptions_[ii];
}
}
info.AddValue(Names.Subscriptions, subscriptions);
}
#endregion
#region Public Methods
/// Returns an unconnected copy of the server with the same OpcUrl.
public override object Clone()
{
// clone the base object.
var clone = (TsCDaServer)base.Clone();
// clone subscriptions.
if (clone.subscriptions_ != null)
{
var subscriptions = new TsCDaSubscriptionCollection();
foreach (TsCDaSubscription subscription in clone.subscriptions_)
{
subscriptions.Add(subscription.Clone());
}
clone.subscriptions_ = subscriptions;
}
// return clone.
return clone;
}
/// Connects to the server with the specified OpcUrl and credentials.
/// If an OPC specific error occur this exception is raised. The Result field includes then the OPC specific code.
/// The network address of the remote server.
/// Any protocol configuration or user authentication information.
public override void Connect(OpcUrl url, OpcConnectData connectData)
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
// connect to server.
base.Connect(url, connectData);
// all done if no subscriptions.
if (subscriptions_ == null)
{
return;
}
// create subscriptions (should only happen if server has been deserialized).
var subscriptions = new TsCDaSubscriptionCollection();
foreach (TsCDaSubscription template in subscriptions_)
{
// create subscription for template.
try
{
subscriptions.Add(EstablishSubscription(template));
}
catch
{
// Ignore exceptions here
}
}
// save new set of subscriptions.
subscriptions_ = subscriptions;
}
/// Disconnects from the server and releases all network resources.
public override void Disconnect()
{
if (Server == null) throw new NotConnectedException();
// dispose of all subscriptions first.
if (subscriptions_ != null)
{
foreach (TsCDaSubscription subscription in subscriptions_)
{
subscription.Dispose();
}
subscriptions_ = null;
}
// disconnect from server.
base.Disconnect();
}
/// Returns the filters applied by the server to any item results returned to the client.
/// A bit mask indicating which fields should be returned in any item results.
public int GetResultFilters()
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
if (Server == null) throw new NotConnectedException();
// update local cache.
filters_ = ((ITsDaServer)Server).GetResultFilters();
// return filters.
return filters_;
}
/// Sets the filters applied by the server to any item results returned to the client.
/// A bit mask indicating which fields should be returned in any item results.
public void SetResultFilters(int filters)
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
if (Server == null) throw new NotConnectedException();
// set filters on server.
((ITsDaServer)Server).SetResultFilters(filters);
// cache updated filters.
filters_ = filters;
}
/// Returns the current server status.
/// The current server status.
public OpcServerStatus GetServerStatus()
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
if (Server == null) throw new NotConnectedException();
var status = ((ITsDaServer)Server).GetServerStatus();
if (status != null)
{
if (status.StatusInfo == null)
{
status.StatusInfo = GetString($"serverState.{status.ServerState}");
}
}
else
{
throw new NotConnectedException();
}
return status;
}
/// Reads the current values for a set of items.
/// The results of the read operation for each item.
/// OPC XML-DA Server or OPC Data Access Server V3.x
/// The set of items to read.
public TsCDaItemValueResult[] Read(TsCDaItem[] items)
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
if (Server == null) throw new NotConnectedException();
return ((ITsDaServer)Server).Read(items);
}
/// Writes the value, quality and timestamp for a set of items.
/// The results of the write operation for each item.
/// OPC XML-DA Server or OPC Data Access Server V3.x
/// The set of item values to write.
public OpcItemResult[] Write(TsCDaItemValue[] items)
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
if (Server == null) throw new NotConnectedException();
return ((ITsDaServer)Server).Write(items);
}
///
/// Creates a new subscription.
///
/// The new subscription object.
/// OPC XML-DA Server or OPC Data Access Server V2.x / V3.x
/// The initial state of the subscription.
public virtual ITsCDaSubscription CreateSubscription(TsCDaSubscriptionState state)
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
if (state == null) throw new ArgumentNullException(nameof(state));
if (Server == null) throw new NotConnectedException();
// create subscription on server.
var subscription = ((ITsDaServer)Server).CreateSubscription(state);
// set filters.
subscription.SetResultFilters(filters_);
// append new subscription to existing list.
var subscriptions = new TsCDaSubscriptionCollection();
if (subscriptions_ != null)
{
foreach (TsCDaSubscription value in subscriptions_)
{
subscriptions.Add(value);
}
}
subscriptions.Add(CreateSubscription(subscription));
// save new subscription list.
subscriptions_ = subscriptions;
// return new subscription.
return subscriptions_[subscriptions_.Count - 1];
}
///
/// Creates a new instance of the appropriate subscription object.
///
/// The remote subscription object.
protected virtual TsCDaSubscription CreateSubscription(ITsCDaSubscription subscription)
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
return new TsCDaSubscription(this, subscription);
}
/// Cancels a subscription and releases all resources allocated for it.
/// OPC XML-DA Server or OPC Data Access Server V2.x / V3.x
/// The subscription to cancel.
public virtual void CancelSubscription(ITsCDaSubscription subscription)
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
if (subscription == null) throw new ArgumentNullException(nameof(subscription));
if (Server == null) throw new NotConnectedException();
// validate argument.
if (!typeof(TsCDaSubscription).IsInstanceOfType(subscription))
{
throw new ArgumentException(@"Incorrect object type.", nameof(subscription));
}
if (!Equals(((TsCDaSubscription)subscription).Server))
{
throw new ArgumentException(@"Server subscription.", nameof(subscription));
}
// search for subscription in list of subscriptions.
var subscriptions = new TsCDaSubscriptionCollection();
foreach (TsCDaSubscription current in subscriptions_)
{
if (!subscription.Equals(current))
{
subscriptions.Add(current);
}
}
// check if subscription was not found.
if (subscriptions.Count == subscriptions_.Count)
{
throw new ArgumentException(@"Subscription not found.", nameof(subscription));
}
// remove subscription from list of subscriptions.
subscriptions_ = subscriptions;
// cancel subscription on server.
((ITsDaServer)Server).CancelSubscription(((TsCDaSubscription)subscription).Subscription);
}
/// Fetches all the children of the root branch that meet the filter criteria.
/// The set of elements found.
/// OPC Data Access Server V2.x / V3.x
/// The filters to use to limit the set of child elements returned.
private TsCDaBrowseElement[] Browse(
TsCDaBrowseFilters filters)
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
if (Server == null) throw new NotConnectedException();
TsCDaBrowsePosition position;
var elementsList = new List();
var elements = ((ITsDaServer)Server).Browse(null, filters, out position);
if (elements != null)
{
Browse(elements, filters, ref elementsList);
}
return elementsList.ToArray();
}
private void Browse(TsCDaBrowseElement[] elements, TsCDaBrowseFilters filters, ref List elementsList)
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
TsCDaBrowsePosition position;
foreach (var element in elements)
{
if (element.HasChildren)
{
var itemId = new OpcItem(element.ItemPath, element.ItemName);
var childElements = ((ITsDaServer)Server).Browse(itemId, filters, out position);
if (childElements != null)
{
Browse(childElements, filters, ref elementsList);
}
}
else
{
elementsList.Add(element);
}
}
}
/// Fetches the children of a branch that meet the filter criteria.
/// The set of elements found.
/// OPC XML-DA Server or OPC Data Access Server V2.x / V3.x
/// The identifier of branch which is the target of the search.
/// The filters to use to limit the set of child elements returned.
/// An object used to continue a browse that could not be completed.
public TsCDaBrowseElement[] Browse(
OpcItem itemId,
TsCDaBrowseFilters filters,
out TsCDaBrowsePosition position)
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
if (Server == null) throw new NotConnectedException();
return ((ITsDaServer)Server).Browse(itemId, filters, out position);
}
/// Continues a browse operation with previously specified search criteria.
/// The set of elements found.
/// OPC XML-DA Server or OPC Data Access Server V2.x / V3.x
/// An object containing the browse operation state information.
public TsCDaBrowseElement[] BrowseNext(ref TsCDaBrowsePosition position)
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
if (Server == null) throw new NotConnectedException();
return ((ITsDaServer)Server).BrowseNext(ref position);
}
/// Returns the item properties for a set of items.
/// A list of item identifiers.
/// A list of properties to fetch for each item.
/// Whether the property values should be returned with the properties.
/// A list of properties for each item.
public TsCDaItemPropertyCollection[] GetProperties(
OpcItem[] itemIds,
TsDaPropertyID[] propertyIDs,
bool returnValues)
{
LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.DataAccess);
if (Server == null) throw new NotConnectedException();
return ((ITsDaServer)Server).GetProperties(itemIds, propertyIDs, returnValues);
}
#endregion
#region Private Methods
///
/// Establishes a subscription based on the template provided.
///
private TsCDaSubscription EstablishSubscription(TsCDaSubscription template)
{
// create subscription.
var subscription = new TsCDaSubscription(this, ((ITsDaServer)Server).CreateSubscription(template.State));
// set filters.
subscription.SetResultFilters(template.Filters);
// add items.
try
{
subscription.AddItems(template.Items);
}
catch
{
subscription.Dispose();
subscription = null;
}
// return new subscription.
return subscription;
}
#endregion
}
}