/// <summary>
 /// Creates a <see cref="MeasurementMetadata"/>
 /// </summary>
 /// <param name="key">Gets or sets the primary key of this <see cref="IMeasurement"/>.</param>
 /// <param name="tagName">Gets or sets the text based tag name of this <see cref="IMeasurement"/>.</param>
 /// <param name="adder">Defines an offset to add to the <see cref="IMeasurement"/> value.</param>
 /// <param name="multiplier">Defines a multiplicative offset to apply to the <see cref="IMeasurement"/> value.</param>
 /// <param name="measurementValueFilter">Gets or sets function used to apply a down-sampling filter over a sequence of <see cref="IMeasurement"/> values.</param>
 public MeasurementMetadata(MeasurementKey key, string tagName, double adder, double multiplier, MeasurementValueFilterFunction measurementValueFilter)
 {
     Key = key;
     TagName = tagName;
     Adder = adder;
     Multiplier = multiplier;
     MeasurementValueFilter = measurementValueFilter;
 }
Beispiel #2
0
 /// <summary>
 /// Constructs a new <see cref="MeasurementKey"/> given the specified parameters.
 /// </summary>
 /// <param name="signalID"><see cref="Guid"/> ID of associated signal, if defined.</param>
 /// <param name="value">A string representation of the <see cref="MeasurementKey"/>.</param>
 /// <param name="key">The measurement key that was created or updated or <see cref="Undefined"/>.</param>
 /// <returns>True if the measurement key was successfully created or updated, false otherwise.</returns>
 /// <exception cref="ArgumentException"><paramref name="signalID"/> cannot be empty.</exception>
 /// <exception cref="ArgumentNullException">Measurement key Source cannot be null.</exception>
 public static bool TryCreateOrUpdate(Guid signalID, string value, out MeasurementKey key)
 {
     try
     {
         key = CreateOrUpdate(signalID, value);
         return(true);
     }
     catch
     {
         key = Undefined;
         return(false);
     }
 }
Beispiel #3
0
        /// <summary>
        /// Initializes the <see cref="PowerCalculator"/>.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            Dictionary<string, string> settings = Settings;
            string setting;

            // Load parameters
            if (settings.TryGetValue("trackRecentValues", out setting))
                m_trackRecentValues = setting.ParseBoolean();
            else
                m_trackRecentValues = true;

            if (settings.TryGetValue("sampleSize", out setting))            // Data sample size to monitor, in seconds
                m_sampleSize = int.Parse(setting);
            else
                m_sampleSize = 5;

            // Load needed phase angle and magnitude measurement keys from defined InputMeasurementKeys
            m_voltageAngle = InputMeasurementKeys.Where((key, index) => InputMeasurementKeyTypes[index] == SignalType.VPHA).FirstOrDefault();
            m_voltageMagnitude = InputMeasurementKeys.Where((key, index) => InputMeasurementKeyTypes[index] == SignalType.VPHM).FirstOrDefault();
            m_currentAngle = InputMeasurementKeys.Where((key, index) => InputMeasurementKeyTypes[index] == SignalType.IPHA).FirstOrDefault();
            m_currentMagnitude = InputMeasurementKeys.Where((key, index) => InputMeasurementKeyTypes[index] == SignalType.IPHM).FirstOrDefault();

            if ((object)m_voltageAngle == null || m_voltageAngle.ID == 0)
                throw new InvalidOperationException("No voltage angle input was defined - one voltage angle input measurement is required for the power calculator.");

            if ((object)m_voltageMagnitude == null || m_voltageMagnitude.ID == 0)
                throw new InvalidOperationException("No voltage magnitude input was defined - one voltage magnitude input measurement is required for the power calculator.");

            if ((object)m_currentAngle == null || m_currentAngle.ID == 0)
                throw new InvalidOperationException("No current angle input was defined - one current angle input measurement is required for the power calculator.");

            if ((object)m_currentMagnitude == null || m_currentMagnitude.ID == 0)
                throw new InvalidOperationException("No current magnitude input measurement was defined - one current magnitude input measurement is required for the power calculator.");

            // Make sure only these four phasor measurements are used as input (any others will be ignored)
            InputMeasurementKeys = new[] { m_voltageAngle, m_voltageMagnitude, m_currentAngle, m_currentMagnitude };

            // Validate output measurements
            if (OutputMeasurements.Length < Enum.GetValues(typeof(Output)).Length)
                throw new InvalidOperationException("Not enough output measurements were specified for the power calculator, expecting measurements for the \"Calculated Power\" and the \"Calculated Reactive Power\" - in this order.");

            if (m_trackRecentValues)
            {
                m_powerSample = new List<double>();
                m_reactivePowerSample = new List<double>();
            }

            // Assign a default adapter name to be used if power calculator is loaded as part of automated collection
            if (string.IsNullOrWhiteSpace(Name))
                Name = string.Format("PC!{0}", OutputMeasurements[(int)Output.Power].Key);
        }
Beispiel #4
0
        private PIPoint CreateMappedPIPoint(MeasurementKey key)
        {
            PIPoint point = null;

            // Map measurement to PI point
            try
            {
                // Two ways to find points here
                // 1. if we are running metadata sync from the adapter, look for the signal ID in the EXDESC field
                // 2. if the pi points are being manually maintained, look for either the point tag or alternate tag in the actual pi point tag
                Guid signalID = key.SignalID;
                bool foundPoint = false;

                if (RunMetadataSync)
                {
                    // Attempt lookup by EXDESC signal ID                           
                    point = GetPIPointBySignalID(m_connection.Server, signalID);
                    foundPoint = (object)point != null;
                }

                if (!foundPoint)
                {
                    // Lookup meta-data for current measurement
                    DataRow[] rows = DataSource.Tables["ActiveMeasurements"].Select($"SignalID='{signalID}'");

                    if (rows.Length > 0)
                    {
                        DataRow measurementRow = rows[0];
                        string tagName = measurementRow["PointTag"].ToNonNullString().Trim();

                        // Use alternate tag if one is defined
                        if (!string.IsNullOrWhiteSpace(measurementRow["AlternateTag"].ToString()) && !measurementRow["SignalType"].ToString().Equals("DIGI", StringComparison.OrdinalIgnoreCase))
                            tagName = measurementRow["AlternateTag"].ToString().Trim();

                        // Attempt lookup by tag name
                        point = GetPIPoint(m_connection.Server, tagName);

                        if ((object)point == null)
                        {
                            if (!m_refreshingMetadata)
                                OnStatusMessage("WARNING: No PI points found for tag '{0}'. Data will not be archived for '{1}'.", tagName, key);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                OnProcessException(new InvalidOperationException($"Failed to map '{key}' to a PI tag: {ex.Message}", ex));
            }

            // If no point could be mapped, return null connection mapping so key can be removed from tag-map
            if ((object)point == null)
                return null;

            return point;
        }
Beispiel #5
0
        // Resets the PI tag to MeasurementKey mapping for loading data into PI by finding PI points that match either the GSFSchema point tag or alternate tag
        private void EstablishPIPointDictionary(MeasurementKey[] inputMeasurements)
        {
            OnStatusMessage("Establishing connection points for mapping...");

            List<MeasurementKey> newTags = new List<MeasurementKey>();

            if ((object)inputMeasurements != null && inputMeasurements.Length > 0)
            {
                foreach (MeasurementKey key in inputMeasurements)
                {
                    // Add key to dictionary with null value if not defined, actual mapping will happen dynamically as needed
                    if (!m_mappedPIPoints.ContainsKey(key))
                        m_mappedPIPoints.TryAdd(key, null);

                    newTags.Add(key);
                }
            }

            if (newTags.Count > 0)
            {
                // Determine which tags no longer exist
                PIPoint removedPIPoint;
                HashSet<MeasurementKey> tagsToRemove = new HashSet<MeasurementKey>(m_mappedPIPoints.Keys);

                // If there are existing tags that are not part of new updates, these need to be removed
                tagsToRemove.ExceptWith(newTags);

                if (tagsToRemove.Count > 0)
                {
                    foreach (MeasurementKey key in tagsToRemove)
                        m_mappedPIPoints.TryRemove(key, out removedPIPoint);

                    OnStatusMessage("Detected {0:N0} tags that have been removed from OSI-PI output - primary tag-map has been updated...", tagsToRemove.Count);
                }

                if (m_mappedPIPoints.Count == 0)
                    OnStatusMessage("WARNING: No PI tags were mapped to measurements - no tag-map exists so no points will be archived.");
            }
            else
            {
                if (m_mappedPIPoints.Count > 0)
                    OnStatusMessage("WARNING: No PI tags were mapped to measurements - existing tag-map with {0:N0} tags remains in use.", m_mappedPIPoints.Count);
                else
                    OnStatusMessage("WARNING: No PI tags were mapped to measurements - no tag-map exists so no points will be archived.");
            }
        }
        /// <summary>
        /// Initializes <see cref="FileExporter"/>.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            Dictionary<string, string> settings = Settings;
            const string errorMessage = "{0} is missing from Settings - Example: exportInterval=5; modelIdentifier=Goslin; referenceAngleMeasurement=DEVARCHIVE:6; inputMeasurementKeys={{FILTER ActiveMeasurements WHERE Device='SHELBY' AND SignalType='FREQ'}}";
            string setting;
            double seconds;

            // Load required parameters
            if (!settings.TryGetValue("exportInterval", out setting) || !double.TryParse(setting, out seconds))
                throw new ArgumentException(string.Format(errorMessage, "exportInterval"));

            if (!settings.TryGetValue("fileExportPath", out m_fileExportPath))
                m_fileExportPath = FilePath.GetAbsolutePath("");

            m_exportInterval = (int)(seconds * 1000.0D);

            if (m_exportInterval <= 0)
                throw new ArgumentException("exportInterval should not be 0 - Example: exportInterval=5.5");

            if ((object)InputMeasurementKeys == null || InputMeasurementKeys.Length == 0)
                throw new InvalidOperationException("There are no input measurements defined. You must define \"inputMeasurementKeys\" to define which measurements to export.");

            // Reference angle measurement has to be defined if using reference angle
            if (!settings.TryGetValue("referenceAngleMeasurement", out setting))
                throw new ArgumentException(string.Format(errorMessage, "referenceAngleMeasurement"));

            m_referenceAngleKey = MeasurementKey.Parse(setting);

            // Make sure reference angle is first angle of input measurement keys collection
            InputMeasurementKeys = (new[] { m_referenceAngleKey }).Concat(InputMeasurementKeys).ToArray();

            // Make sure sure reference angle key is actually an angle measurement
            SignalType signalType = InputMeasurementKeyTypes[InputMeasurementKeys.IndexOf(key => key == m_referenceAngleKey)];

            if (signalType != SignalType.IPHA && signalType != SignalType.VPHA)
                throw new InvalidOperationException($"Specified reference angle measurement key is a {signalType.GetFormattedName()} signal, not a phase angle.");

            Comments = settings.TryGetValue("comments", out setting) ? setting : "Comment section---";

            if (!settings.TryGetValue("modelIdentifier", out setting))
                throw new ArgumentException(string.Format(errorMessage, "modelIdentifier"));

            ModelIdentifier = setting;

            // We enable tracking of latest measurements so we can use these values if points are missing - since we are using
            // latest measurement tracking, we sort all incoming points even though most of them will be thrown out...
            TrackLatestMeasurements = true;

            //// Create a new dictionary of base voltages
            //m_baseVoltages = new Dictionary<MeasurementKey, double>();

            StringBuilder header = new StringBuilder();
            //MeasurementKey voltageMagnitudeKey;
            //double baseKV;

            // Write header row
            header.Append("TimeStamp");

            DataTable measurements = DataSource.Tables["ActiveMeasurements"];
            int tieLines = 0;
            bool referenceAdded = false;

            for (int i = 0; i < InputMeasurementKeys.Length; i++)
            {
                // Lookup measurement key in active measurements table
                DataRow row = measurements.Select($"ID='{InputMeasurementKeys[i]}'")[0];
                string deviceName = row["Device"].ToNonNullString("UNDEFINED").ToUpper().Trim();

                if (!referenceAdded && InputMeasurementKeys[i] == m_referenceAngleKey)
                {
                    header.AppendFormat(",Ref. Angle of {0}", deviceName);
                    referenceAdded = true;
                }
                else
                    switch (InputMeasurementKeyTypes[i])
                    {
                        case SignalType.VPHM:
                            header.AppendFormat(",{0} |V|", deviceName);
                            tieLines++;

                            //voltageMagnitudeKey = InputMeasurementKeys[i];

                            //if (settings.TryGetValue(voltageMagnitudeKey + "BaseKV", out setting) && double.TryParse(setting, out baseKV))
                            //{
                            //    m_baseVoltages.Add(voltageMagnitudeKey, baseKV * SI.Kilo);
                            //}
                            //else
                            //{
                            //    int baseKVCode;

                            //    // Second check if base KV can be inferred from device name suffixed KV index
                            //    if (int.TryParse(deviceName[deviceName.Length - 1].ToString(), out baseKVCode) && baseKVCode > 1 && baseKVCode < BaseKVs.Length)
                            //    {
                            //        m_baseVoltages.Add(voltageMagnitudeKey, BaseKVs[baseKVCode]);
                            //    }
                            //    else
                            //    {
                            //        OnStatusMessage("WARNING: Did not find a valid base KV setting for voltage magnitude {0}, assumed 500KV", voltageMagnitudeKey.ToString());
                            //        m_baseVoltages.Add(voltageMagnitudeKey, 500.0D * SI.Kilo);
                            //    }
                            //}
                            break;
                        case SignalType.VPHA:
                            header.AppendFormat(",{0} Voltage Angle", deviceName);
                            break;
                        case SignalType.IPHM:
                            header.AppendFormat(",{0} |I|", deviceName);
                            break;
                        case SignalType.IPHA:
                            header.AppendFormat(",{0} Current Angle", deviceName);
                            break;
                        default:
                            header.AppendFormat(",{0} ??", deviceName);
                            break;
                    }
            }

            string row5 = header.ToString();
            header = new StringBuilder();

            // Add row 1
            header.AppendFormat("Comments: {0}\r\n", Comments);

            // Add row 2
            header.AppendFormat("Model Identifier: {0}\r\n", ModelIdentifier);

            // Add row 3
            header.Append("Datapoints,Tielines,TimeStep");

            if (InputMeasurementKeys.Length - 3 > 0)
                header.Append(new string(',', InputMeasurementKeys.Length - 3));

            header.AppendLine();

            // Add row 4
            header.AppendFormat("{0},{1},{2}", RowCountMarker, tieLines, 1.0D / FramesPerSecond);
            header.AppendLine();

            // Add row 5
            header.AppendLine(row5);

            // Cache header for each file export
            m_header = header.ToString();
        }
Beispiel #7
0
 /// <summary>
 /// Creates a new instance of <see cref="MeasurementMetadata"/> using the provided measurement <paramref name="key"/>. All other fields remain the same.
 /// </summary>
 /// <param name="key">The key to set.</param>
 /// <returns>New instance of <see cref="MeasurementMetadata"/> using the provided measurement <paramref name="key"/>.</returns>
 public MeasurementMetadata ChangeKey(MeasurementKey key) => Key == key ? this : new MeasurementMetadata(key, TagName, Adder, Multiplier, MeasurementValueFilter);
 /// <summary>
 /// Get a collection of out-of-range <see cref="IMeasurement"/>s with the given key.
 /// </summary>
 /// <param name="key">The <see cref="MeasurementKey"/> corresponding to the desired measurements.</param>
 /// <returns>A collection of out-of-range <see cref="IMeasurement"/>s.</returns>
 public ICollection<IMeasurement> GetOutOfRangeMeasurements(MeasurementKey key)
 {
     lock (m_outOfRangeMeasurements)
     {
         PurgeOldMeasurements(key);
         return new LinkedList<IMeasurement>(m_outOfRangeMeasurements[key]);
     }
 }
Beispiel #9
0
 /// <summary>
 /// Constructs a new <see cref="Measurement"/> using default settings.
 /// </summary>
 public Measurement()
 {
     m_key = MeasurementKey.Undefined;
     m_receivedTimestamp = DateTime.UtcNow.Ticks;
     m_multiplier        = 1.0D;
 }
        /// <summary>
        /// Constructs a new <see cref="MeasurementKey"/> given the specified parameters.
        /// </summary>
        /// <param name="signalID"><see cref="Guid"/> ID of associated signal, if defined.</param>
        /// <param name="source">Source of the measurement that this <see cref="MeasurementKey"/> represents (e.g., name of archive).</param>
        /// <param name="id">Numeric ID of the measurement that this <see cref="MeasurementKey"/> represents.</param>
        /// <exception cref="ArgumentException"><paramref name="signalID"/> cannot be empty.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> cannot be null.</exception>
        public static MeasurementKey CreateOrUpdate(Guid signalID, string source, uint id)
        {
            Func<Guid, MeasurementKey> addValueFactory;
            Func<Guid, MeasurementKey, MeasurementKey> updateValueFactory;

            if (signalID == Guid.Empty)
                throw new ArgumentException("Unable to update undefined measurement key", nameof(signalID));

            if (string.IsNullOrWhiteSpace(source))
                throw new ArgumentNullException(nameof(source), "MeasurementKey source cannot be null or empty");

            addValueFactory = guid =>
            {
                // Create a new measurement key and add it to the KeyCache
                ConcurrentDictionary<uint, MeasurementKey> idLookup = s_keyCache.GetOrAdd(source, s => new ConcurrentDictionary<uint, MeasurementKey>());
                return idLookup[id] = new MeasurementKey(guid, id, source);
            };

            updateValueFactory = (guid, key) =>
            {
                ConcurrentDictionary<uint, MeasurementKey> idLookup;

                // If existing measurement key is exactly the same as the
                // one we are trying to create, simply return that key
                if (key.ID == id && key.Source == source)
                    return key;

                // Update source and ID and re-insert it into the KeyCache
                key.m_source = source;
                key.m_id = id;

                idLookup = s_keyCache.GetOrAdd(source, s => new ConcurrentDictionary<uint, MeasurementKey>());
                idLookup[id] = key;

                return key;
            };

            // https://msdn.microsoft.com/en-us/library/ee378675(v=vs.110).aspx
            //
            //     If you call AddOrUpdate simultaneously on different threads,
            //     addValueFactory may be called multiple times, but its key/value
            //     pair might not be added to the dictionary for every call.
            //
            // This lock prevents race conditions that might occur in the addValueFactory that
            // could cause different MeasurementKey objects to be written to the KeyCache and IDCache
            lock (s_syncEdits)
            {
                return s_idCache.AddOrUpdate(signalID, addValueFactory, updateValueFactory);
            }
        }
Beispiel #11
0
        // Adds the given mapping to the key-variable map.
        private void AddMapping(MeasurementKey key, string alias)
        {
            if (m_variableNames.Contains(alias))
                throw new ArgumentException(string.Format("Variable name is not unique: {0}", alias));

            m_variableNames.Add(alias);
            m_keyMapping.Add(key, alias);
        }
 /// <summary>
 /// Sets the associated <see cref="MeasurementKey"/> for a <see cref="IMeasurement"/>.
 /// </summary>
 /// <param name="measurement"><see cref="IMeasurement"/> to create new <see cref="MeasurementMetadata"/> for.</param>
 /// <param name="key">New measurement key value to assign to measurement's metadata.</param>
 public static void SetKey(this IMeasurement measurement, MeasurementKey key)
 {
     measurement.Metadata = measurement.Metadata.ChangeKey(key);
 }
 // Static Constructor
 static MeasurementKey()
 {
     // Order of operations is critical here since MeasurementKey and MeasurementMetadata have a circular reference
     Undefined = CreateUndefinedMeasurementKey();
     MeasurementMetadata.CreateUndefinedMeasurementMetadata();
     Undefined.m_metadata = MeasurementMetadata.Undefined;
 }
Beispiel #14
0
        private void HandleNewMeasurementsRequest(MeasurementKey[] Keys)
        {
            OnStatusMessage("Received request for {0} keys...", new object[] { Keys.Count() });

            if (!IsConnected)
                AttemptConnection();

            var query = from row in DataSource.Tables["ActiveMeasurements"].AsEnumerable()
                        from key in Keys
                        where row["ID"].ToString().Split(':')[1] == key.ID.ToString()
                        select new
                        {
                            Key = key,
                            AlternateTag = row["ALTERNATETAG"].ToString(),
                            PointTag = row["POINTTAG"].ToString()
                        };

            StringBuilder tagFilter = new StringBuilder();
            foreach (var row in query)
            {
                string tagname = row.PointTag;
                if (!String.IsNullOrWhiteSpace(row.AlternateTag))
                    tagname = row.AlternateTag;

                if (!m_tagKeyMap.ContainsKey(tagname))
                {
                    m_tagKeyMap.AddOrUpdate(tagname, row.Key, (k, v) => row.Key);
                }

                if (tagFilter.Length > 0)
                    tagFilter.Append(" OR ");

                tagFilter.Append(string.Format("tag='{0}'", tagname));
            }

            m_points = new PIPointList(PIPoint.FindPIPoints(m_connection.Server, tagFilter.ToString(), true));

            // TODO: Re-enable event pipe functionality in AF-SDK
            //bool useEventPipes;

            //// event pipes are only applicable if enabled in connection string and this is a real time session, not playback
            //useEventPipes = m_useEventPipes && StartTimeConstraint == DateTime.MinValue && StopTimeConstraint == DateTime.MaxValue;

            //if (useEventPipes)
            //{
            //    try
            //    {
            //        if (m_pipe != null)
            //            ((_DEventPipeEvents_Event)m_pipe).OnNewValue -= (_DEventPipeEvents_OnNewValueEventHandler)PipeOnOnNewValue;

            //        m_connection.Execute(server => m_pipe = m_points.Data.EventPipe);

            //        ((_DEventPipeEvents_Event)m_pipe).OnNewValue += (_DEventPipeEvents_OnNewValueEventHandler)PipeOnOnNewValue;
            //    }
            //    catch (ThreadAbortException)
            //    {
            //        throw;
            //    }
            //    catch (Exception e)
            //    {
            //        useEventPipes = false; // try to run with polling instead of event pipes;
            //        OnProcessException(e);
            //    }
            //}

            //if (!useEventPipes)
            //{
            // warn that we are going to use a different configuration here...
            if (m_useEventPipes)
                OnStatusMessage("WARNING: PI adapter switching from event pipes to polling due to error or start/stop time constraints.");

            // TODO: Poll method needs some work...
            // set up a new thread to do some long calls to PI and set up threads, timers, etc for polling
            StopGettingData();
            ThreadPool.QueueUserWorkItem(StartGettingData, tagFilter);
            //}

            //m_useEventPipes = useEventPipes;
        }
Beispiel #15
0
        // Add a measurement to the list of out of range measurements corresponding to the given key.
        private void AddOutOfRangeMeasurement(MeasurementKey key, IMeasurement measurement)
        {
            lock (m_outOfRangeMeasurements)
            {
                LinkedList<IMeasurement> outOfRangeList;

                if (!m_outOfRangeMeasurements.TryGetValue(key, out outOfRangeList))
                {
                    outOfRangeList = new LinkedList<IMeasurement>();
                    m_outOfRangeMeasurements.Add(key, outOfRangeList);
                }

                outOfRangeList.AddLast(measurement);
            }
        }
Beispiel #16
0
        // Purge old, out-of-range measurements with the given key.
        private void PurgeOldMeasurements(MeasurementKey key)
        {
            lock (m_outOfRangeMeasurements)
            {
                LinkedList<IMeasurement> measurements = m_outOfRangeMeasurements[key];
                bool donePurging = false;

                // Purge old measurements to prevent redundant warnings.
                while (measurements.Count > 0 && !donePurging)
                {
                    IMeasurement measurement = measurements.First.Value;
                    Ticks diff = base.RealTime - measurement.Timestamp;

                    if (diff >= base.LagTicks + m_timeToPurge)
                        measurements.RemoveFirst();
                    else
                        donePurging = true;
                }
            }
        }
 /// <summary>
 /// Creates a new instance of <see cref="MeasurementMetadata"/> using the provided measurement <paramref name="key"/>. All other fields remain the same.
 /// </summary>
 /// <param name="key">The key to set.</param>
 /// <returns>New instance of <see cref="MeasurementMetadata"/> using the provided measurement <paramref name="key"/>.</returns>
 public MeasurementMetadata ChangeKey(MeasurementKey key) => Key == key ? this : new MeasurementMetadata(key, TagName, Adder, Multiplier, MeasurementValueFilter);
Beispiel #18
0
        // Lookup signal type for given measurement key
        private SignalType LookupSignalType(MeasurementKey key)
        {
            try
            {
                DataRow[] filteredRows = DataSource.Tables["ActiveMeasurements"].Select(string.Format("ID = '{0}'", key));

                if (filteredRows.Length > 0)
                    return (SignalType)Enum.Parse(typeof(SignalType), filteredRows[0]["SignalType"].ToString(), true);
            }
            catch (Exception ex)
            {
                OnProcessException(new InvalidOperationException(string.Format("Failed to lookup signal type for measurement {0}: {1}", key, ex.Message), ex));
            }

            return SignalType.NONE;
        }
 /// <summary>
 /// Constructs a new <see cref="MeasurementKey"/> given the specified parameters.
 /// </summary>
 /// <param name="signalID"><see cref="Guid"/> ID of associated signal, if defined.</param>
 /// <param name="value">A string representation of the <see cref="MeasurementKey"/>.</param>
 /// <param name="key">The measurement key that was created or updated or <see cref="Undefined"/>.</param>
 /// <returns>True if the measurement key was successfully created or updated, false otherwise.</returns>
 /// <exception cref="ArgumentException"><paramref name="signalID"/> cannot be empty.</exception>
 /// <exception cref="ArgumentNullException">Measurement key Source cannot be null.</exception>
 public static bool TryCreateOrUpdate(Guid signalID, string value, out MeasurementKey key)
 {
     try
     {
         key = CreateOrUpdate(signalID, value);
         return true;
     }
     catch
     {
         key = Undefined;
         return false;
     }
 }
Beispiel #20
0
 /// <summary>
 /// Constructs a new <see cref="Measurement"/> using default settings.
 /// </summary>
 public Measurement()
 {
     m_key = MeasurementKey.Undefined;
     m_receivedTimestamp = DateTime.UtcNow.Ticks;
     m_multiplier = 1.0D;
 }
        /// <summary>
        /// Initializes <see cref="FileExporter"/>.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            Dictionary<string, string> settings = Settings;
            const string errorMessage = "{0} is missing from Settings - Example: exportInterval=5; useReferenceAngle=True; referenceAngleMeasurement=DEVARCHIVE:6; companyTagPrefix=TVA; useNumericQuality=True; inputMeasurementKeys={{FILTER ActiveMeasurements WHERE Device='SHELBY' AND SignalType='FREQ'}}";
            string setting;
            double seconds;

            // Load required parameters
            if (!settings.TryGetValue("exportInterval", out setting) || !double.TryParse(setting, out seconds))
                throw new ArgumentException(string.Format(errorMessage, "exportInterval"));

            m_exportInterval = (int)(seconds * 1000.0D);
            m_lastPublicationTime = 0;

            if (m_exportInterval <= 0)
                throw new ArgumentException("exportInterval should not be 0 - Example: exportInterval=5.5");

            if (InputMeasurementKeys == null || InputMeasurementKeys.Length == 0)
                throw new InvalidOperationException("There are no input measurements defined. You must define \"inputMeasurementKeys\" to define which measurements to export.");

            if (!settings.TryGetValue("useReferenceAngle", out setting))
                throw new ArgumentException(string.Format(errorMessage, "useReferenceAngle"));

            m_useReferenceAngle = setting.ParseBoolean();

            if (m_useReferenceAngle)
            {
                // Reference angle measurement has to be defined if using reference angle
                if (!settings.TryGetValue("referenceAngleMeasurement", out setting))
                    throw new ArgumentException(string.Format(errorMessage, "referenceAngleMeasurement"));

                m_referenceAngleKey = MeasurementKey.Parse(setting);

                // Make sure reference angle is part of input measurement keys collection
                if (!InputMeasurementKeys.Contains(m_referenceAngleKey))
                    InputMeasurementKeys = InputMeasurementKeys.Concat(new[] { m_referenceAngleKey }).ToArray();

                // Make sure reference angle key is actually an angle measurement
                SignalType signalType = InputMeasurementKeyTypes[InputMeasurementKeys.IndexOf(key => key == m_referenceAngleKey)];

                if (signalType != SignalType.IPHA && signalType != SignalType.VPHA)
                    throw new InvalidOperationException($"Specified reference angle measurement key is a {signalType.GetFormattedName()} signal, not a phase angle.");
            }

            // Load optional parameters
            if (settings.TryGetValue("companyTagPrefix", out setting))
                m_companyTagPrefix = setting.ToUpper().Trim();
            else
                m_companyTagPrefix = null;

            if (settings.TryGetValue("useNumericQuality", out setting))
                m_useNumericQuality = setting.ParseBoolean();
            else
                m_useNumericQuality = false;

            // Suffix company tag prefix with an underscore if defined
            if (!string.IsNullOrWhiteSpace(m_companyTagPrefix))
                m_companyTagPrefix = m_companyTagPrefix.EnsureEnd('_');

            // Define a default export location - user can override and add multiple locations in config later...
            m_dataExporter = new MultipleDestinationExporter(ConfigurationSection, m_exportInterval);
            m_dataExporter.StatusMessage += m_dataExporter_StatusMessage;
            m_dataExporter.ProcessException += m_dataExporter_ProcessException;
            m_dataExporter.Initialize(new[] { new ExportDestination(FilePath.GetAbsolutePath(ConfigurationSection + ".txt"), false) });

            // Create new measurement tag name dictionary
            m_measurementTags = new ConcurrentDictionary<MeasurementKey, string>();
            string pointID = "undefined";

            // Lookup point tag name for input measurement in the ActiveMeasurements table
            foreach (MeasurementKey key in InputMeasurementKeys)
            {
                try
                {
                    // Get measurement key as a string
                    pointID = key.ToString();

                    // Lookup measurement key in active measurements table
                    DataRow row = DataSource.Tables["ActiveMeasurements"].Select($"ID='{pointID}'")[0];

                    // Remove invalid symbols that may be in tag name
                    string pointTag = row["PointTag"].ToNonNullString(pointID).Replace('-', '_').Replace(':', '_').ToUpper();

                    // Prefix point tag with company prefix if defined
                    if (!string.IsNullOrWhiteSpace(m_companyTagPrefix) && !pointTag.StartsWith(m_companyTagPrefix))
                        pointTag = m_companyTagPrefix + pointTag;

                    m_measurementTags.TryAdd(key, pointTag);
                }
                catch (Exception ex)
                {
                    OnProcessException(MessageLevel.Warning, new InvalidOperationException($"Failed to lookup point tag for measurement [{pointID}] due to exception: {ex.Message}"));
                }
            }

            // We enable tracking of latest measurements so we can use these values if points are missing - since we are using
            // latest measurement tracking, we sort all incoming points even though most of them will be thrown out...
            TrackLatestMeasurements = true;
        }
 /// <summary>
 /// Constructs a new <see cref="MeasurementKey"/> given the specified parameters.
 /// </summary>
 /// <param name="signalID"><see cref="Guid"/> ID of associated signal, if defined.</param>
 /// <param name="source">Source of the measurement that this <see cref="MeasurementKey"/> represents (e.g., name of archive).</param>
 /// <param name="id">Numeric ID of the measurement that this <see cref="MeasurementKey"/> represents.</param>
 /// <param name="key">The measurement key that was created or updated or <see cref="Undefined"/>.</param>
 /// <returns>True if the measurement key was successfully created or updated, false otherwise.</returns>
 /// <exception cref="ArgumentException"><paramref name="signalID"/> cannot be empty.</exception>
 /// <exception cref="ArgumentNullException"><paramref name="source"/> cannot be null.</exception>
 public static bool TryCreateOrUpdate(Guid signalID, string source, uint id, out MeasurementKey key)
 {
     try
     {
         key = CreateOrUpdate(signalID, source, id);
         return true;
     }
     catch
     {
         key = Undefined;
         return false;
     }
 }
        /// <summary>
        /// Initializes the <see cref="LossOfField"/> detector.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            Dictionary<string, string> settings = Settings;
            string setting;

            // Load parameters
            if (settings.TryGetValue("pSet", out setting))
                m_pSet = double.Parse(setting);
            else
                m_pSet = -600;

            if (settings.TryGetValue("qSet", out setting))
                m_qSet = double.Parse(setting);
            else
                m_qSet = 200;

            if (settings.TryGetValue("qAreaSet", out setting))
                m_qAreaSet = double.Parse(setting);
            else
                m_qAreaSet = 500;

            if (settings.TryGetValue("voltageThreshold", out setting))
                m_voltageThreshold = double.Parse(setting);
            else
                m_voltageThreshold = 475000;

            if (settings.TryGetValue("analysisInterval", out setting))
                m_analysisInterval = int.Parse(setting);
            else
                m_analysisInterval = FramesPerSecond;

            m_count = 0;
            m_count1 = 0;
            m_count2 = 0;

            // Load needed measurement keys from defined InputMeasurementKeys
            int index;

            // Get expected voltage magnitude
            index = InputMeasurementKeyTypes.IndexOf(signalType => signalType == SignalType.VPHM);
            if (index < 0)
                throw new InvalidOperationException("No voltage magnitude input measurement key was not found - this is a required input measurement for the loss of field detector.");

            m_voltageMagnitude = InputMeasurementKeys[index];

            // Get expected voltage angle
            index = InputMeasurementKeyTypes.IndexOf(signalType => signalType == SignalType.VPHA);
            if (index < 0)
                throw new InvalidOperationException("No voltage angle input measurement key was not found - this is a required input measurement for the loss of field detector.");

            m_voltageAngle = InputMeasurementKeys[index];

            // Get expected current magnitude
            index = InputMeasurementKeyTypes.IndexOf(signalType => signalType == SignalType.IPHM);
            if (index < 0)
                throw new InvalidOperationException("No current magnitude input measurement key was not found - this is a required input measurement for the loss of field detector.");

            m_currentMagnitude = InputMeasurementKeys[index];

            // Get expected current angle
            index = InputMeasurementKeyTypes.IndexOf(signalType => signalType == SignalType.IPHA);
            if (index < 0)
                throw new InvalidOperationException("No current angle input measurement key was not found - this is a required input measurement for the loss of field detector.");

            m_currentAngle = InputMeasurementKeys[index];

            // Make sure only these phasor measurements are used as input
            InputMeasurementKeys = new[] { m_voltageMagnitude, m_voltageAngle, m_currentMagnitude, m_currentAngle };

            // Validate output measurements
            if (OutputMeasurements.Length < Enum.GetValues(typeof(Output)).Length)
                throw new InvalidOperationException("Not enough output measurements were specified for the loss of field detector, expecting measurements for \"Warning Signal Status (0 = Not Signaled, 1 = Signaled)\", \"Real Power\", \"Reactive Power\" and \"Q-Area Value\" - in this order.");
        }
        /// <summary>
        /// Attempts to convert the string representation of a <see cref="MeasurementKey"/> into its value equivalent.
        /// </summary>
        /// <param name="value">A string representing the <see cref="MeasurementKey"/> to convert.</param>
        /// <param name="key">Output <see cref="MeasurementKey"/> in which to stored parsed value.</param>
        /// <returns>A <c>true</c> if <see cref="MeasurementKey"/>representation contained in <paramref name="value"/> could be parsed; otherwise <c>false</c>.</returns>
        public static bool TryParse(string value, out MeasurementKey key)
        {
            string source;
            uint id;

            // Split the input into source and ID
            if (TrySplit(value, out source, out id))
            {
                // First attempt to look up an existing key
                key = LookUpBySource(source, id);

                if (key == Undefined)
                {
                    try
                    {
                        // Lookup failed - attempt to create it with a newly generated signal ID
                        key = CreateOrUpdate(Guid.NewGuid(), source, id);
                    }
                    catch
                    {
                        // source is null or empty
                        key = Undefined;
                    }
                }
            }
            else
            {
                // Incorrect format for a measurement key
                key = Undefined;
            }

            return key != Undefined;
        }
Beispiel #25
0
        private void SubscribeToPointUpdates(MeasurementKey[] keys)
        {
            OnStatusMessage("Subscribing to updates for {0} measurements...", keys.Length);

            var query = from row in DataSource.Tables["ActiveMeasurements"].AsEnumerable()
                        from key in keys
                        where row["ID"].ToString() == key.ToString()
                        select new
                        {
                            Key = key,
                            AlternateTag = row["AlternateTag"].ToString(),
                            PointTag = row["PointTag"].ToString()
                        };

            List<PIPoint> dataPoints = new List<PIPoint>();

            foreach (var row in query)
            {
                string tagName = row.PointTag;

                if (!string.IsNullOrWhiteSpace(row.AlternateTag))
                    tagName = row.AlternateTag;

                OnStatusMessage("DEBUG: Looking up point tag '{0}'...", tagName);

                PIPoint point = GetPIPoint(m_connection.Server, tagName);

                if ((object)point != null)
                {
                    OnStatusMessage("DEBUG: Found point tag '{0}'...", tagName);
                    dataPoints.Add(point);
                    m_tagKeyMap[point.ID] = row.Key;
                }
                else
                {
                    OnStatusMessage("DEBUG: Failed to find point tag '{0}'...", tagName);
                }
            }

            // Remove sign-ups for any existing point list
            if ((object)m_dataPoints != null)
                m_dataPipe.RemoveSignups(m_dataPoints);

            // Sign up for updates on selected points
            AFListResults<PIPoint, AFDataPipeEvent> initialEvents = m_dataPipe.AddSignupsWithInitEvents(dataPoints);

            OnStatusMessage("DEBUG: Initial event count = {0}...", initialEvents.Results.Count);

            foreach (AFDataPipeEvent item in initialEvents.Results)
            {
                OnStatusMessage("DEBUG: Found initial event for action...", item.Action);

                if (item.Action != AFDataPipeAction.Delete)
                    m_dataUpdateObserver_DataUpdated(this, new EventArgs<AFValue>(item.Value));
            }

            m_dataPoints = dataPoints;

            m_eventTimer.Enabled = true;
        }
 /// <summary>
 /// Creates the undefined measurement key. Used to initialize <see cref="Undefined"/>.
 /// </summary>
 private static MeasurementKey CreateUndefinedMeasurementKey()
 {
     MeasurementKey key = new MeasurementKey(Guid.Empty, uint.MaxValue, "__");
     // Lock on s_syncEdits is not required since method is only called by the static constructor
     s_keyCache.GetOrAdd(key.Source, kcf => new ConcurrentDictionary<uint, MeasurementKey>())[uint.MaxValue] = key;
     return key;
 }
Beispiel #27
0
 /// <summary>
 /// Sets the associated <see cref="MeasurementKey"/> for a <see cref="IMeasurement"/>.
 /// </summary>
 /// <param name="measurement"><see cref="IMeasurement"/> to create new <see cref="MeasurementMetadata"/> for.</param>
 /// <param name="key">New measurement key value to assign to measurement's metadata.</param>
 public static void SetKey(this IMeasurement measurement, MeasurementKey key)
 {
     measurement.Metadata = measurement.Metadata.ChangeKey(key);
 }
Beispiel #28
0
        private void EstablishPIPointMappings(MeasurementKey[] keys)
        {
            PIPoint point;
            bool mappingEstablished = false;

            foreach (MeasurementKey key in keys)
            {
                // If adapter gets disabled while executing this thread - go ahead and exit
                if (!Enabled)
                    return;

                try
                {
                    mappingEstablished = false;

                    if (m_mappedPIPoints.TryGetValue(key, out point))
                    {
                        if ((object)point == null)
                        {
                            // Create new connection point
                            point = CreateMappedPIPoint(key);

                            if ((object)point != null)
                            {
                                m_mappedPIPoints[key] = point;
                                mappingEstablished = true;
                            }
                        }
                        else
                        {
                            // Connection point is already established
                            mappingEstablished = true;
                        }
                    }
                }
                catch (Exception ex)
                {
                    OnProcessException(new InvalidOperationException($"Failed to create connection point mapping for '{key}': {ex.Message}", ex));
                }
                finally
                {
                    lock (m_pendingMappings)
                    {
                        m_pendingMappings.Remove(key);
                    }

                    if (!mappingEstablished)
                        m_mappedPIPoints.TryRemove(key, out point);

                    // Provide some level of feed back on progress of mapping process
                    Interlocked.Increment(ref m_processedMappings);

                    if (Interlocked.Read(ref m_processedMappings) % 100 == 0)
                        OnStatusMessage("Mapped {0:N0} PI tags to measurements, {1:0.00%} complete...", m_mappedPIPoints.Count - m_pendingMappings.Count, 1.0D - m_pendingMappings.Count / (double)m_mappedPIPoints.Count);
                }
            }
        }