#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; using System.Runtime.Serialization; #endregion namespace Technosoftware.DaAeHdaClient.Ae { /// /// An in-process object which provides access to AE server objects. /// [Serializable] public class TsCAeServer : OpcServer, ITsCAeServer { #region SubscriptionCollection Class /// /// A read-only collection of subscriptions. /// public class SubscriptionCollection : OpcReadOnlyCollection { #region Constructors, Destructor, Initialization /// /// Creates an empty collection. /// internal SubscriptionCollection() : base(new TsCAeSubscription[0]) { } #endregion #region Public Methods /// /// An indexer for the collection. /// public new TsCAeSubscription this[int index] => (TsCAeSubscription)Array.GetValue(index); /// /// Returns a copy of the collection as an array. /// public new TsCAeSubscription[] ToArray() { return (TsCAeSubscription[])Array; } /// /// Adds a subscription to the end of the collection. /// internal void Add(TsCAeSubscription subscription) { var array = new TsCAeSubscription[Count + 1]; Array.CopyTo(array, 0); array[Count] = subscription; Array = array; } /// /// Removes a subscription to the from the collection. /// internal void Remove(TsCAeSubscription subscription) { var array = new TsCAeSubscription[Count - 1]; var index = 0; for (var ii = 0; ii < Array.Length; ii++) { var element = (TsCAeSubscription)Array.GetValue(ii); if (subscription != element) { array[index++] = element; } } Array = array; } #endregion } #endregion #region Names Class /// /// A set of names for fields used in serialization. /// private class Names { internal const string Count = "CT"; internal const string Subscription = "SU"; } #endregion #region Fields private int filters_; private bool disposing_; private SubscriptionCollection subscriptions_ = new SubscriptionCollection(); #endregion #region Constructors, Destructor, Initialization /// /// 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 TsCAeServer(OpcFactory factory, OpcUrl url) : base(factory, url) { } /// /// Constructs a server by de-serializing its OpcUrl from the stream. /// protected TsCAeServer(SerializationInfo info, StreamingContext context) : base(info, context) { var count = (int)info.GetValue(Names.Count, typeof(int)); subscriptions_ = new SubscriptionCollection(); for (var ii = 0; ii < count; ii++) { var subscription = (TsCAeSubscription)info.GetValue(Names.Subscription + ii, typeof(TsCAeSubscription)); subscriptions_.Add(subscription); } } #endregion #region Properties /// /// The filters supported by the server. /// // ReSharper disable once UnusedMember.Global public int AvailableFilters => filters_; /// /// The outstanding subscriptions for the server. /// public SubscriptionCollection Subscriptions => subscriptions_; #endregion #region Public Methods /// /// Connects to the server with the specified OpcUrl and credentials. /// public override void Connect(OpcUrl url, OpcConnectData connectData) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); // connect to server. base.Connect(url, connectData); // all done if no subscriptions. if (subscriptions_.Count == 0) { return; } // create subscriptions (should only happen if server has been deserialized). var subscriptions = new SubscriptionCollection(); foreach (TsCAeSubscription template in subscriptions_) { // create subscription for template. try { subscriptions.Add(EstablishSubscription(template)); } catch { // ignored } } // 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. disposing_ = true; foreach (TsCAeSubscription subscription in subscriptions_) { subscription.Dispose(); } disposing_ = false; // disconnect from server. base.Disconnect(); } /// /// Returns the current server status. /// /// The current server status. public OpcServerStatus GetServerStatus() { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); var status = ((ITsCAeServer)Server).GetServerStatus(); if (status != null) { if (status.StatusInfo == null) { status.StatusInfo = GetString($"serverState.{status.ServerState}"); } } else { if (Server == null) throw new NotConnectedException(); } return status; } /// /// Creates a new event subscription. /// /// The initial state for the subscription. /// The new subscription object. public ITsCAeSubscription CreateSubscription(TsCAeSubscriptionState state) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); // create remote object. var subscription = ((ITsCAeServer)Server).CreateSubscription(state); if (subscription != null) { // create wrapper. var wrapper = new TsCAeSubscription(this, subscription, state); subscriptions_.Add(wrapper); return wrapper; } // should never happen. return null; } /// /// Returns the event filters supported by the server. /// /// A bit mask of all event filters supported by the server. public int QueryAvailableFilters() { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); filters_ = ((ITsCAeServer)Server).QueryAvailableFilters(); return filters_; } /// /// Returns the event categories supported by the server for the specified event types. /// /// A bit mask for the event types of interest. /// A collection of event categories. public TsCAeCategory[] QueryEventCategories(int eventType) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); // fetch categories from server. var categories = ((ITsCAeServer)Server).QueryEventCategories(eventType); // return result. return categories; } /// /// Returns the condition names supported by the server for the specified event categories. /// /// A bit mask for the event categories of interest. /// A list of condition names. public string[] QueryConditionNames(int eventCategory) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); // fetch condition names from the server. var conditions = ((ITsCAeServer)Server).QueryConditionNames(eventCategory); // return result. return conditions; } /// /// Returns the sub-condition names supported by the server for the specified event condition. /// /// The name of the condition. /// A list of sub-condition names. public string[] QuerySubConditionNames(string conditionName) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); // fetch sub-condition names from the server. var subConditions = ((ITsCAeServer)Server).QuerySubConditionNames(conditionName); // return result. return subConditions; } /// /// Returns the condition names supported by the server for the specified event source. /// /// The name of the event source. /// A list of condition names. public string[] QueryConditionNames(string sourceName) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); // fetch condition names from the server. var conditions = ((ITsCAeServer)Server).QueryConditionNames(sourceName); // return result. return conditions; } /// /// Returns the event attributes supported by the server for the specified event categories. /// /// A bit mask for the event categories of interest. /// A collection of event attributes. public TsCAeAttribute[] QueryEventAttributes(int eventCategory) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); // fetch attributes from server. var attributes = ((ITsCAeServer)Server).QueryEventAttributes(eventCategory); // return result. return attributes; } /// /// Returns the DA item ids for a set of attribute ids belonging to events which meet the specified filter criteria. /// /// The event source of interest. /// The id of the event category for the events of interest. /// The name of a condition within the event category. /// The name of a sub-condition within a multi-state condition. /// The ids of the attributes to return item ids for. /// A list of item urls for each specified attribute. public TsCAeItemUrl[] TranslateToItemIDs( string sourceName, int eventCategory, string conditionName, string subConditionName, int[] attributeIDs) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); var itemUrls = ((ITsCAeServer)Server).TranslateToItemIDs( sourceName, eventCategory, conditionName, subConditionName, attributeIDs); return itemUrls; } /// /// Returns the current state information for the condition instance corresponding to the source and condition name. /// /// The source name /// A condition name for the source. /// The list of attributes to return with the condition state. /// The current state of the connection. public TsCAeCondition GetConditionState( string sourceName, string conditionName, int[] attributeIDs) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); var condition = ((ITsCAeServer)Server).GetConditionState(sourceName, conditionName, attributeIDs); return condition; } /// /// Places the specified process areas into the enabled state. /// /// A list of fully qualified area names. /// The results of the operation for each area. public OpcResult[] EnableConditionByArea(string[] areas) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); var results = ((ITsCAeServer)Server).EnableConditionByArea(areas); return results; } /// /// Places the specified process areas into the disabled state. /// /// A list of fully qualified area names. /// The results of the operation for each area. public OpcResult[] DisableConditionByArea(string[] areas) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); var results = ((ITsCAeServer)Server).DisableConditionByArea(areas); return results; } /// /// Places the specified process areas into the enabled state. /// /// A list of fully qualified source names. /// The results of the operation for each area. public OpcResult[] EnableConditionBySource(string[] sources) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); var results = ((ITsCAeServer)Server).EnableConditionBySource(sources); return results; } /// /// Places the specified process areas into the disabled state. /// /// A list of fully qualified source names. /// The results of the operation for each area. public OpcResult[] DisableConditionBySource(string[] sources) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); var results = ((ITsCAeServer)Server).DisableConditionBySource(sources); return results; } /// /// Returns the enabled state for the specified process areas. /// /// A list of fully qualified area names. public TsCAeEnabledStateResult[] GetEnableStateByArea(string[] areas) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); var results = ((ITsCAeServer)Server).GetEnableStateByArea(areas); return results; } /// /// Returns the enabled state for the specified event sources. /// /// A list of fully qualified source names. public TsCAeEnabledStateResult[] GetEnableStateBySource(string[] sources) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); var results = ((ITsCAeServer)Server).GetEnableStateBySource(sources); return results; } /// /// Used to acknowledge one or more conditions in the event server. /// /// The identifier for who is acknowledging the condition. /// A comment associated with the acknowledgment. /// The conditions being acknowledged. /// A list of result id indicating whether each condition was successfully acknowledged. public OpcResult[] AcknowledgeCondition( string acknowledgmentId, string comment, TsCAeEventAcknowledgement[] conditions) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); return ((ITsCAeServer)Server).AcknowledgeCondition(acknowledgmentId, comment, conditions); } /// /// Browses for all children of the specified area that meet the filter criteria. /// /// The full-qualified id for the area. /// The type of children to return. /// The expression used to filter the names of children returned. /// The set of elements that meet the filter criteria. public TsCAeBrowseElement[] Browse( string areaId, TsCAeBrowseType browseType, string browseFilter) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); return ((ITsCAeServer)Server).Browse(areaId, browseType, browseFilter); } /// /// Browses for all children of the specified area that meet the filter criteria. /// /// The full-qualified id for the area. /// The type of children to return. /// The expression used to filter the names of children returned. /// The maximum number of elements to return. /// The object used to continue the browse if the number nodes exceeds the maximum specified. /// The set of elements that meet the filter criteria. public TsCAeBrowseElement[] Browse( string areaId, TsCAeBrowseType browseType, string browseFilter, int maxElements, out IOpcBrowsePosition position) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); return ((ITsCAeServer)Server).Browse(areaId, browseType, browseFilter, maxElements, out position); } /// /// Continues browsing the server's address space at the specified position. /// /// The maximum number of elements to return. /// The position object used to continue a browse operation. /// The set of elements that meet the filter criteria. public TsCAeBrowseElement[] BrowseNext(int maxElements, ref IOpcBrowsePosition position) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (Server == null) throw new NotConnectedException(); return ((ITsCAeServer)Server).BrowseNext(maxElements, ref position); } #endregion #region ISerializable Members /// /// Serializes a server into a stream. /// public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue(Names.Count, subscriptions_.Count); for (var ii = 0; ii < subscriptions_.Count; ii++) { info.AddValue(Names.Subscription + ii, subscriptions_[ii]); } } #endregion #region Private Methods /// /// Called when a subscription object is disposed. /// /// internal void SubscriptionDisposed(TsCAeSubscription subscription) { if (!disposing_) { subscriptions_.Remove(subscription); } } /// /// Establishes a subscription based on the template provided. /// private TsCAeSubscription EstablishSubscription(TsCAeSubscription template) { ITsCAeSubscription remoteServer = null; try { // create remote object. remoteServer = ((ITsCAeServer)Server).CreateSubscription(template.State); if (remoteServer == null) { return null; } // create wrapper. var subscription = new TsCAeSubscription(this, remoteServer, template.State); // set filters. subscription.SetFilters(template.Filters); // set attributes. var enumerator = template.Attributes.GetEnumerator(); while (enumerator.MoveNext()) { if (enumerator.Key != null) subscription.SelectReturnedAttributes( (int)enumerator.Key, ((TsCAeSubscription.AttributeCollection)enumerator.Value).ToArray()); } // return new subscription return subscription; } catch { if (remoteServer != null) { remoteServer.Dispose(); } } // return null. return null; } #endregion } }