//****************************************************************************************************** // ObservationRecord.cs - Gbtc // // Copyright © 2012, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 05/03/2012 - Stephen C. Wills, Grid Protection Alliance // Generated original version of source code. // 12/17/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.Linq; using System.Text; using GSF.PQDIF.Physical; namespace GSF.PQDIF.Logical { #region [ Enumerations ] /// /// Type of trigger which caused the observation. /// public enum TriggerMethod : uint { /// /// No trigger. /// None = 0u, /// /// A specific channel (or channels) caused the trigger; should be /// used with tagChannelTriggerIdx to specify which channels. /// Channel = 1u, /// /// Periodic data trigger. /// Periodic = 2u, /// /// External system trigger. /// External = 3u, /// /// Periodic statistical data. /// PeriodicStats = 4u } #endregion /// /// Represents an observation record in a PQDIF file. /// public class ObservationRecord { #region [ Members ] // Fields private readonly Record m_physicalRecord; private readonly DataSourceRecord m_dataSource; private readonly MonitorSettingsRecord m_settings; #endregion #region [ Constructors ] /// /// Creates a new instance of the class. /// /// The physical structure of the observation record. /// The data source record that defines the channels in this observation record. /// The monitor settings to be applied to this observation record. private ObservationRecord(Record physicalRecord, DataSourceRecord dataSource, MonitorSettingsRecord settings) { m_physicalRecord = physicalRecord; m_dataSource = dataSource; m_settings = settings; } #endregion #region [ Properties ] /// /// Gets the physical structure of the observation record. /// public Record PhysicalRecord { get { return m_physicalRecord; } } /// /// Gets the data source record that defines /// the channels in this observation record. /// public DataSourceRecord DataSource { get { return m_dataSource; } } /// /// Gets the monitor settings record that defines the /// settings to be applied to this observation record. /// public MonitorSettingsRecord Settings { get { return m_settings; } } /// /// Gets the name of the observation record. /// public string Name { get { CollectionElement collectionElement = m_physicalRecord.Body.Collection; VectorElement nameElement = collectionElement.GetVectorByTag(ObservationNameTag); return Encoding.ASCII.GetString(nameElement.GetValues()).Trim((char)0); } set { CollectionElement collectionElement = m_physicalRecord.Body.Collection; byte[] bytes = Encoding.ASCII.GetBytes(value + (char)0); collectionElement.AddOrUpdateVector(ObservationNameTag, PhysicalType.Char1, bytes); } } /// /// Gets the creation time of the observation record. /// public DateTime CreateTime { get { return m_physicalRecord.Body.Collection .GetScalarByTag(TimeCreateTag) .GetTimestamp(); } set { CollectionElement collectionElement = m_physicalRecord.Body.Collection; ScalarElement timeCreateElement = collectionElement.GetOrAddScalar(TimeCreateTag); timeCreateElement.TypeOfValue = PhysicalType.Timestamp; timeCreateElement.SetTimestamp(value); } } /// /// Gets the starting time of the data in the observation record. This time should /// be used as the base time for all relative seconds recorded in the series instances. /// /// /// The contains two timestamp fields: and /// . The StartTime is a required part of any Observation, whereas the /// is optional. /// /// The does not have to be the same as the trigger time and can therefore /// be chosen more or less arbitrarily. For instance, you can choose to record the /// as the timestamp of the first data point in the observation or the top /// of an interval in which the data was captured. The trigger time field is more well defined, /// essentially always representing the point in time at which the data source decided to capture /// some data in the PQDIF file. /// public DateTime StartTime { get { return m_physicalRecord.Body.Collection .GetScalarByTag(TimeStartTag) .GetTimestamp(); } set { CollectionElement collectionElement = m_physicalRecord.Body.Collection; ScalarElement timeStartElement = collectionElement.GetOrAddScalar(TimeStartTag); timeStartElement.TypeOfValue = PhysicalType.Timestamp; timeStartElement.SetTimestamp(value); } } /// /// Gets or sets the type of trigger which caused the observation. /// public TriggerMethod TriggerMethod { get { return (TriggerMethod)m_physicalRecord.Body.Collection .GetScalarByTag(TriggerMethodTag) .GetUInt4(); } set { CollectionElement collectionElement = m_physicalRecord.Body.Collection; ScalarElement triggerMethodElement = collectionElement.GetOrAddScalar(TriggerMethodTag); triggerMethodElement.TypeOfValue = PhysicalType.UnsignedInteger4; triggerMethodElement.SetUInt4((uint)value); } } /// /// Gets the time the observation was triggered. For more information regarding recording /// time in PQD files, see . /// public DateTime TimeTriggered { get { ScalarElement timeTriggeredElement = m_physicalRecord.Body.Collection .GetScalarByTag(TimeTriggeredTag); if ((object)timeTriggeredElement == null) return DateTime.MinValue; return timeTriggeredElement.GetTimestamp(); } set { CollectionElement collectionElement = m_physicalRecord.Body.Collection; ScalarElement timeTriggeredElement = collectionElement.GetOrAddScalar(TimeTriggeredTag); timeTriggeredElement.TypeOfValue = PhysicalType.Timestamp; timeTriggeredElement.SetTimestamp(value); } } /// /// Gets or sets the Disturbance Category ID /// public Guid DisturbanceCategoryID { get { CollectionElement collectionElement = m_physicalRecord.Body.Collection; ScalarElement DisturbanceIDElement = collectionElement.GetScalarByTag(DisturbanceCategoryTag); if ((object)DisturbanceIDElement == null) return DisturbanceCategory.None; return DisturbanceIDElement.GetGuid(); } set { CollectionElement collectionElement = m_physicalRecord.Body.Collection; ScalarElement vendorIDElement = collectionElement.GetOrAddScalar(DisturbanceCategoryTag); vendorIDElement.TypeOfValue = PhysicalType.Guid; vendorIDElement.SetGuid(value); } } /// /// Gets or sets the index into collection within this record which initiated the observation. /// public uint[] ChannelTriggerIndex { get { VectorElement channelTriggerIndexElement = m_physicalRecord.Body.Collection .GetVectorByTag(ChannelTriggerIndexTag); if ((object)channelTriggerIndexElement == null) return new uint[0]; return Enumerable.Range(0, channelTriggerIndexElement.Size) .Select(index => channelTriggerIndexElement.GetUInt4(index)) .ToArray(); } set { CollectionElement collectionElement = m_physicalRecord.Body.Collection; VectorElement channelTriggerIndexElement = collectionElement.GetOrAddVector(ChannelTriggerIndexTag); channelTriggerIndexElement.TypeOfValue = PhysicalType.UnsignedInteger4; channelTriggerIndexElement.Size = value.Length; for (int i = 0; i < value.Length; i++) channelTriggerIndexElement.SetUInt4(i, value[i]); } } /// /// Gets the channel instances in this observation record. /// public IList ChannelInstances { get { return m_physicalRecord.Body.Collection .GetCollectionByTag(ChannelInstancesTag) .GetElementsByTag(OneChannelInstanceTag) .Cast() .Select(collection => new ChannelInstance(collection, this)) .ToList(); } } #endregion #region [ Methods ] /// /// Adds a new channel instance to the collection /// of channel instances in this observation record. /// public ChannelInstance AddNewChannelInstance(ChannelDefinition channelDefinition) { CollectionElement channelInstanceElement = new CollectionElement() { TagOfElement = OneChannelInstanceTag }; ChannelInstance channelInstance = new ChannelInstance(channelInstanceElement, this); channelInstance.ChannelDefinitionIndex = (uint)channelDefinition.DataSource.ChannelDefinitions.IndexOf(channelDefinition); channelInstanceElement.AddElement(new CollectionElement() { TagOfElement = ChannelInstance.SeriesInstancesTag }); CollectionElement channelInstancesElement = m_physicalRecord.Body.Collection.GetCollectionByTag(ChannelInstancesTag); if ((object)channelInstancesElement == null) { channelInstancesElement = new CollectionElement() { TagOfElement = ChannelInstancesTag }; m_physicalRecord.Body.Collection.AddElement(channelInstancesElement); } channelInstancesElement.AddElement(channelInstanceElement); return channelInstance; } /// /// Removes the given channel instance from the collection of channel instances. /// /// The channel instance to be removed. public void Remove(ChannelInstance channelInstance) { CollectionElement channelInstancesElement = m_physicalRecord.Body.Collection.GetCollectionByTag(ChannelInstancesTag); List channelInstanceElements; ChannelInstance instance; if ((object)channelInstancesElement == null) return; channelInstanceElements = channelInstancesElement.GetElementsByTag(OneChannelInstanceTag).Cast().ToList(); foreach (CollectionElement channelSettingElement in channelInstanceElements) { instance = new ChannelInstance(channelSettingElement, this); if (Equals(channelInstance, instance)) channelInstancesElement.RemoveElement(channelSettingElement); } } #endregion #region [ Static ] // Static Fields /// /// Tag that identifies the name of the observation record. /// public static readonly Guid ObservationNameTag = new Guid("3d786f8a-f76e-11cf-9d89-0080c72e70a3"); /// /// Tag that identifies the time that the observation record was created. /// public static readonly Guid TimeCreateTag = new Guid("3d786f8b-f76e-11cf-9d89-0080c72e70a3"); /// /// Tag that identifies the start time of the data in the observation record. /// public static readonly Guid TimeStartTag = new Guid("3d786f8c-f76e-11cf-9d89-0080c72e70a3"); /// /// Tag that identifies the type of trigger that caused the observation. /// public static readonly Guid TriggerMethodTag = new Guid("3d786f8d-f76e-11cf-9d89-0080c72e70a3"); /// /// Tag that identifies the time the observation was triggered. /// public static readonly Guid TimeTriggeredTag = new Guid("3d786f8e-f76e-11cf-9d89-0080c72e70a3"); /// /// Tag that identifies the index into collection within this record. This specifies which channel(s) initiated the observation. /// public static readonly Guid ChannelTriggerIndexTag = new Guid("3d786f8f-f76e-11cf-9d89-0080c72e70a3"); /// /// Tag that identifies the channel instances collection. /// public static readonly Guid ChannelInstancesTag = new Guid("3d786f91-f76e-11cf-9d89-0080c72e70a3"); /// /// Tag that identifies a single channel instance in the collection. /// public static readonly Guid OneChannelInstanceTag = new Guid("3d786f92-f76e-11cf-9d89-0080c72e70a3"); /// /// Tag that identifies the Disturbance Category. /// public static readonly Guid DisturbanceCategoryTag = new Guid("b48d8597-f5f5-11cf-9d89-0080c72e70a3"); // Static Methods /// /// Creates a new observation record from scratch with the given data source and settings. /// /// The data source record that defines the channels in this observation record. /// The monitor settings to be applied to this observation record. /// The new observation record. public static ObservationRecord CreateObservationRecord(DataSourceRecord dataSource, MonitorSettingsRecord settings) { Guid recordTypeTag = Record.GetTypeAsTag(RecordType.Observation); Record physicalRecord = new Record(recordTypeTag); ObservationRecord observationRecord = new ObservationRecord(physicalRecord, dataSource, settings); DateTime now = DateTime.UtcNow; observationRecord.Name = now.ToString(); observationRecord.CreateTime = now; observationRecord.StartTime = now; observationRecord.TriggerMethod = TriggerMethod.None; CollectionElement bodyElement = physicalRecord.Body.Collection; bodyElement.AddElement(new CollectionElement() { TagOfElement = ChannelInstancesTag }); return observationRecord; } /// /// Creates a new observation record from the given physical record /// if the physical record is of type observation. Returns null if /// it is not. /// /// The physical record used to create the observation record. /// The data source record that defines the channels in this observation record. /// The monitor settings to be applied to this observation record. /// The new observation record, or null if the physical record does not define a observation record. public static ObservationRecord CreateObservationRecord(Record physicalRecord, DataSourceRecord dataSource, MonitorSettingsRecord settings) { bool isValidObservationRecord = physicalRecord.Header.TypeOfRecord == RecordType.Observation; return isValidObservationRecord ? new ObservationRecord(physicalRecord, dataSource, settings) : null; } #endregion } }