#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 subscription objects. /// [Serializable] public class TsCAeSubscription : ITsCAeSubscription, ISerializable, ICloneable { #region CategoryCollection Class /// /// Contains a read-only collection category ids. /// public class CategoryCollection : OpcReadOnlyCollection { #region Constructors, Destructor, Initialization /// /// Creates an empty collection. /// internal CategoryCollection() : base(new int[0]) { } /// /// Creates a collection containing the list of category ids. /// internal CategoryCollection(int[] categoryIDs) : base(categoryIDs) { } #endregion #region Public Methods /// /// An indexer for the collection. /// public new int this[int index] => (int)Array.GetValue(index); /// /// Returns a copy of the collection as an array. /// public new int[] ToArray() { return (int[])OpcConvert.Clone(Array); } #endregion } #endregion #region StringCollection Class /// /// Contains a read-only collection of strings. /// public class StringCollection : OpcReadOnlyCollection { #region Constructors, Destructor, Initialization /// /// Creates an empty collection. /// internal StringCollection() : base(new string[0]) { } /// /// Creates a collection containing the specified strings. /// internal StringCollection(string[] strings) : base(strings) { } #endregion #region Public Methods /// /// An indexer for the collection. /// public new string this[int index] => (string)Array.GetValue(index); /// /// Returns a copy of the collection as an array. /// public new string[] ToArray() { return (string[])OpcConvert.Clone(Array); } #endregion } #endregion #region AttributeDictionary Class /// /// Contains a read-only dictionary of attribute lists indexed by category id. /// [Serializable] public class AttributeDictionary : OpcReadOnlyDictionary { #region Constructors, Destructor, Initialization /// /// Creates an empty collection. /// internal AttributeDictionary() : base(null) { } /// /// Constructs an dictionary from a set of category ids. /// internal AttributeDictionary(Hashtable dictionary) : base(dictionary) { } #endregion #region Public Methods /// /// Gets or sets the attribute collection for the specified category. /// public AttributeCollection this[int categoryId] => (AttributeCollection)base[categoryId]; /// /// Adds or replaces the set of attributes associated with the category. /// internal void Update(int categoryId, int[] attributeIDs) { Dictionary[categoryId] = new AttributeCollection(attributeIDs); } #endregion #region ISerializable Members /// /// Constructs an object by deserializing it from a stream. /// protected AttributeDictionary(SerializationInfo info, StreamingContext context) : base(info, context) { } #endregion } #endregion #region AttributeCollection Class /// /// Contains a read-only collection attribute ids. /// [Serializable] public class AttributeCollection : OpcReadOnlyCollection { #region Constructors, Destructor, Initialization /// /// Creates an empty collection. /// internal AttributeCollection() : base(new int[0]) { } /// /// Creates a collection containing the specified attribute ids. /// internal AttributeCollection(int[] attributeIDs) : base(attributeIDs) { } #endregion #region Public Methods /// /// An indexer for the collection. /// public new int this[int index] => (int)Array.GetValue(index); /// /// Returns a copy of the collection as an array. /// public new int[] ToArray() { return (int[])OpcConvert.Clone(Array); } #endregion #region ISerializable Members /// /// Constructs an object by deserializing it from a stream. /// protected AttributeCollection(SerializationInfo info, StreamingContext context) : base(info, context) { } #endregion } #endregion #region Names Class /// /// A set of names for fields used in serialization. /// private class Names { internal const string State = "ST"; internal const string Filters = "FT"; internal const string Attributes = "AT"; } #endregion #region Fields private bool disposed_; private TsCAeServer server_; private ITsCAeSubscription subscription_; // state private TsCAeSubscriptionState state_; private string name_; // filters private TsCAeSubscriptionFilters subscriptionFilters_ = new TsCAeSubscriptionFilters(); private CategoryCollection categories_ = new CategoryCollection(); private StringCollection areas_ = new StringCollection(); private StringCollection sources_ = new StringCollection(); // returned attributes private AttributeDictionary attributes_ = new AttributeDictionary(); #endregion #region Constructors, Destructor, Initialization /// /// Initializes object with default values. /// public TsCAeSubscription(TsCAeServer server, ITsCAeSubscription subscription, TsCAeSubscriptionState state) { server_ = server ?? throw new ArgumentNullException(nameof(server)); subscription_ = subscription ?? throw new ArgumentNullException(nameof(subscription)); state_ = (TsCAeSubscriptionState)state.Clone(); name_ = state.Name; } /// /// The finalizer implementation. /// ~TsCAeSubscription() { Dispose(false); } /// /// /// public virtual void Dispose() { Dispose(true); // Take yourself off the Finalization queue // to prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } /// /// Dispose(bool disposing) executes in two distinct scenarios. /// If disposing equals true, the method has been called directly /// or indirectly by a user's code. Managed and unmanaged resources /// can be disposed. /// If disposing equals false, the method has been called by the /// runtime from inside the finalizer and you should not reference /// other objects. Only unmanaged resources can be disposed. /// /// If true managed and unmanaged resources can be disposed. If false only unmanaged resources. protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!disposed_) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { if (subscription_ != null) { server_.SubscriptionDisposed(this); subscription_.Dispose(); } } // Release unmanaged resources. If disposing is false, // only the following code is executed. } disposed_ = true; } #endregion #region Properties /// /// The server that the subscription object belongs to. /// public TsCAeServer Server => server_; /// /// A descriptive name for the subscription. /// public string Name => state_.Name; /// /// A unique identifier for the subscription assigned by the client. /// public object ClientHandle => state_.ClientHandle; /// /// Whether the subscription is monitoring for events to send to the client. /// public bool Active => state_.Active; /// /// The maximum rate at which the server send event notifications. /// The ApplicationInstance.TimeAsUtc property defines /// the time format (UTC or local time). /// public int BufferTime => state_.BufferTime; /// /// The requested maximum number of events that will be sent in a single callback. /// public int MaxSize => state_.MaxSize; /// /// The maximum period between updates sent to the client. /// public int KeepAlive => state_.KeepAlive; /// /// A mask indicating which event types should be sent to the client. /// public int EventTypes => subscriptionFilters_.EventTypes; /// /// The highest severity for the events that should be sent to the client. /// // ReSharper disable once UnusedMember.Global public int HighSeverity => subscriptionFilters_.HighSeverity; /// /// The lowest severity for the events that should be sent to the client. /// // ReSharper disable once UnusedMember.Global public int LowSeverity => subscriptionFilters_.LowSeverity; /// /// The event category ids monitored by this subscription. /// public CategoryCollection Categories => categories_; /// /// A list of full-qualified ids for process areas of interest - only events or conditions in these areas will be reported. /// public StringCollection Areas => areas_; /// /// A list of full-qualified ids for sources of interest - only events or conditions from these sources will be reported. /// public StringCollection Sources => sources_; /// /// The list of attributes returned for each event category. /// public AttributeDictionary Attributes => attributes_; #endregion #region Public Methods /// /// Returns a writable copy of the current attributes. /// public TsCAeAttributeDictionary GetAttributes() { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); var attributes = new TsCAeAttributeDictionary(); var enumerator = attributes_.GetEnumerator(); while (enumerator.MoveNext()) { if (enumerator.Key != null) { var categoryId = (int)enumerator.Key; var attributeIDs = (AttributeCollection)enumerator.Value; attributes.Add(categoryId, attributeIDs.ToArray()); } } return attributes; } #endregion #region ISerializable Members /// /// Constructs a server by de-serializing its OpcUrl from the stream. /// protected TsCAeSubscription(SerializationInfo info, StreamingContext context) { state_ = (TsCAeSubscriptionState)info.GetValue(Names.State, typeof(TsCAeSubscriptionState)); subscriptionFilters_ = (TsCAeSubscriptionFilters)info.GetValue(Names.Filters, typeof(TsCAeSubscriptionFilters)); attributes_ = (AttributeDictionary)info.GetValue(Names.Attributes, typeof(AttributeDictionary)); name_ = state_.Name; categories_ = new CategoryCollection(subscriptionFilters_.Categories.ToArray()); areas_ = new StringCollection(subscriptionFilters_.Areas.ToArray()); sources_ = new StringCollection(subscriptionFilters_.Sources.ToArray()); } /// /// Serializes a server into a stream. /// public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue(Names.State, state_); info.AddValue(Names.Filters, subscriptionFilters_); info.AddValue(Names.Attributes, attributes_); } #endregion #region ICloneable Members /// /// Returns an unconnected copy of the subscription with the same items. /// public virtual object Clone() { // do a memberwise clone. var clone = (TsCAeSubscription)MemberwiseClone(); /* // place clone in disconnected state. clone.server = null; clone.subscription = null; clone.state = (SubscriptionState)state.Clone(); // clear server handles. clone.state.ServerHandle = null; // always make cloned subscriptions inactive. clone.state.Active = false; // clone items. if (clone.items != null) { ArrayList items = new ArrayList(); foreach (Item item in clone.items) { items.Add(item.Clone()); } clone.items = (Item[])items.ToArray(typeof(Item)); } */ // return clone. return clone; } #endregion #region ISubscription Members /// /// An event to receive data change updates. /// public event TsCAeDataChangedEventHandler DataChangedEvent { add => subscription_.DataChangedEvent += value; remove => subscription_.DataChangedEvent -= value; } /// /// Returns the current state of the subscription. /// /// The current state of the subscription. public TsCAeSubscriptionState GetState() { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (subscription_ == null) throw new NotConnectedException(); state_ = subscription_.GetState(); state_.Name = name_; return (TsCAeSubscriptionState)state_.Clone(); } /// /// Changes the state of a subscription. /// /// A bit mask that indicates which elements of the subscription state are changing. /// The new subscription state. /// The actual subscription state after applying the changes. public TsCAeSubscriptionState ModifyState(int masks, TsCAeSubscriptionState state) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (subscription_ == null) throw new NotConnectedException(); state_ = subscription_.ModifyState(masks, state); if ((masks & (int)TsCAeStateMask.Name) != 0) { state_.Name = name_ = state.Name; } else { state_.Name = name_; } return (TsCAeSubscriptionState)state_.Clone(); } /// /// Returns the current filters for the subscription. /// /// The current filters for the subscription. public TsCAeSubscriptionFilters GetFilters() { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (subscription_ == null) throw new NotConnectedException(); subscriptionFilters_ = subscription_.GetFilters(); categories_ = new CategoryCollection(subscriptionFilters_.Categories.ToArray()); areas_ = new StringCollection(subscriptionFilters_.Areas.ToArray()); sources_ = new StringCollection(subscriptionFilters_.Sources.ToArray()); return (TsCAeSubscriptionFilters)subscriptionFilters_.Clone(); } /// /// Sets the current filters for the subscription. /// /// The new filters to use for the subscription. public void SetFilters(TsCAeSubscriptionFilters filters) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (subscription_ == null) throw new NotConnectedException(); subscription_.SetFilters(filters); GetFilters(); } /// /// Returns the set of attributes to return with event notifications. /// /// The set of attributes to returned with event notifications. public int[] GetReturnedAttributes(int eventCategory) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (subscription_ == null) throw new NotConnectedException(); var attributeIDs = subscription_.GetReturnedAttributes(eventCategory); attributes_.Update(eventCategory, (int[])OpcConvert.Clone(attributeIDs)); return attributeIDs; } /// /// Selects the set of attributes to return with event notifications. /// /// The specific event category for which the attributes apply. /// The list of attribute ids to return. public void SelectReturnedAttributes(int eventCategory, int[] attributeIDs) { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (subscription_ == null) throw new NotConnectedException(); subscription_.SelectReturnedAttributes(eventCategory, attributeIDs); attributes_.Update(eventCategory, (int[])OpcConvert.Clone(attributeIDs)); } /// /// Force a refresh for all active conditions and inactive, unacknowledged conditions whose event notifications match the filter of the event subscription. /// public void Refresh() { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (subscription_ == null) throw new NotConnectedException(); subscription_.Refresh(); } /// /// Cancels an outstanding refresh request. /// public void CancelRefresh() { LicenseHandler.ValidateFeatures(LicenseHandler.ProductFeature.AlarmsConditions); if (subscription_ == null) throw new NotConnectedException(); subscription_.CancelRefresh(); } #endregion #region Internal Properties /// /// The current state. /// internal TsCAeSubscriptionState State => state_; /// /// The current filters. /// internal TsCAeSubscriptionFilters Filters => subscriptionFilters_; #endregion } }