//******************************************************************************************************
// 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
}
}