Beispiel #1
0
        /// <summary>
        /// Get signal reference for specified <see cref="SignalKind"/>.
        /// </summary>
        /// <param name="type"><see cref="SignalKind"/> to request signal reference for.</param>
        /// <returns>Signal reference of given <see cref="SignalKind"/>.</returns>
        public string GetSignalReference(SignalKind type)
        {
            // We cache non-indexed signal reference strings so they don't need to be generated at each mapping call.
            // This helps with performance since the mappings for each signal occur 30 times per second.
            string[] references;
            int      typeIndex = (int)type;

            // Look up synonym in dictionary based on signal type, if found return single element
            references = m_generatedSignalReferenceCache[typeIndex];

            if ((object)references != null)
            {
                return(references[0]);
            }

            // Create a new signal reference array (for single element)
            references = new string[1];

            // Create and cache new non-indexed signal reference
            references[0] = SignalReference.ToString(IDLabel, type);

            // Cache generated signal synonym
            m_generatedSignalReferenceCache[typeIndex] = references;

            return(references[0]);
        }
 /// <summary>
 /// Constructs a new <see cref="SignalReferenceMeasurement"/> from the specified parameters.
 /// </summary>
 /// <param name="measurement">Source <see cref="IMeasurement"/> value.</param>
 /// <param name="signalReference">Associated <see cref="SignalReference"/>.</param>
 public SignalReferenceMeasurement(IMeasurement measurement, SignalReference signalReference)
     : base(measurement.ID, measurement.Source, measurement.Value, measurement.Adder, measurement.Multiplier, measurement.Timestamp)
 {
     base.ValueQualityIsGood = measurement.ValueQualityIsGood;
     base.TimestampQualityIsGood = measurement.TimestampQualityIsGood;
     m_signalReference = signalReference;
 }
Beispiel #3
0
        /// <summary>
        /// Get signal reference for specified <see cref="SignalKind"/> and <paramref name="index"/>.
        /// </summary>
        /// <param name="type"><see cref="SignalKind"/> to request signal reference for.</param>
        /// <param name="index">Index <see cref="SignalKind"/> to request signal reference for.</param>
        /// <param name="count">Number of signals defined for this <see cref="SignalKind"/>.</param>
        /// <returns>Signal reference of given <see cref="SignalKind"/> and <paramref name="index"/>.</returns>
        public string GetSignalReference(SignalKind type, int index, int count)
        {
            // We cache indexed signal reference strings so they don't need to be generated at each mapping call.
            // This helps with performance since the mappings for each signal occur 30 times per second.
            // For speed purposes we intentionally do not validate that signalIndex falls within signalCount, be
            // sure calling procedures are very careful with parameters...
            string[] references;
            int      typeIndex = (int)type;

            // Look up synonym in dictionary based on signal type
            references = m_generatedSignalReferenceCache[typeIndex];

            if ((object)references != null)
            {
                // Verify signal count has not changed (we may have received new configuration from device)
                if (count == references.Length)
                {
                    // Create and cache new signal reference if it doesn't exist
                    if ((object)references[index] == null)
                    {
                        references[index] = SignalReference.ToString(IDLabel, type, index + 1);
                    }

                    return(references[index]);
                }
            }

            // Create a new indexed signal reference array
            references = new string[count];

            // Create and cache new signal reference
            references[index] = SignalReference.ToString(IDLabel, type, index + 1);

            // Cache generated signal synonym array
            m_generatedSignalReferenceCache[typeIndex] = references;

            return(references[index]);
        }
Beispiel #4
0
        public void ValidateCalculatorConfigurations(int?historianID, string systemName)
        {
            const int Avg = 0, Max = 1, Min = 2;

            PowerCalculationConfigurationValidation.ValidateDatabaseDefinitions();

            TableOperations <Measurement> measurementTable = DataContext.Table <Measurement>();
            string frequencyDeviceName = string.Format(SystemFrequencyDeviceName, systemName);

            // Look for existing frequency average
            if (measurementTable.QueryRecordCountWhere($"SignalReference = '{SignalReference.ToString(frequencyDeviceName, SignalKind.Frequency)}'") > 0)
            {
                return;
            }

            TableOperations <CustomActionAdapter> customActionAdapterTable = DataContext.Table <CustomActionAdapter>();
            CustomActionAdapter avgFreqAdapter = customActionAdapterTable.QueryRecordWhere("TypeName = {0}", typeof(PowerCalculations.AverageFrequency).FullName) ?? NewCustomActionAdapter();

            Measurement[] measurements = GetCalculatedFrequencyMeasurements(historianID, systemName, frequencyDeviceName);

            double lagTime             = DefaultCalculationLagTime;

            // Reduce lag-time since dynamic calculations can depend on average frequency
            lagTime -= lagTime > 1.0 ? 1.0 : 0.5;

            if (lagTime < 0.1)
            {
                lagTime = 0.1;
            }

            avgFreqAdapter.AdapterName      = "PHASOR!AVERAGEFREQ";
            avgFreqAdapter.AssemblyName     = "PowerCalculations.dll";
            avgFreqAdapter.TypeName         = typeof(PowerCalculations.AverageFrequency).FullName;
            avgFreqAdapter.ConnectionString = $"InputMeasurementKeys={{FILTER ActiveMeasurements WHERE SignalType = 'FREQ' AND SignalReference NOT LIKE '{frequencyDeviceName}%'}}; OutputMeasurements={{{measurements[Avg].SignalID};{measurements[Max].SignalID};{measurements[Min].SignalID}}}; LagTime={lagTime}; LeadTime={DefaultCalculationLeadTime}; FramesPerSecond={DefaultCalculationFramesPerSecond}";
            avgFreqAdapter.Enabled          = true;

            customActionAdapterTable.AddNewOrUpdateRecord(avgFreqAdapter);
        }
Beispiel #5
0
        private Measurement[] GetCalculatedFrequencyMeasurements(int?historianID)
        {
            SignalType freqSignalType = DataContext.Table <SignalType>().QueryRecordWhere("Acronym = 'FREQ'");

            if (freqSignalType.ID == 0)
            {
                throw new InvalidOperationException("Failed to find 'FREQ' signal type");
            }

            Device freqDevice = QueryDevice(SystemFrequencyDeviceName);

            freqDevice.Acronym        = SystemFrequencyDeviceName;
            freqDevice.Name           = "Calculated System Frequency Statistics Virtual Device";
            freqDevice.IsConcentrator = false;
            freqDevice.HistorianID    = historianID;
            freqDevice.ProtocolID     = VirtualProtocolID;
            freqDevice.Enabled        = true;

            AddNewOrUpdateDevice(freqDevice);

            freqDevice = QueryDevice(SystemFrequencyDeviceName);

            // Signal references within a device are used to map frequencies back into a frame of data - since frames are only
            // designated to have a single frequency measurement, the max and min frequencies are marked as analog values
            string avgFreqSignalRef = SignalReference.ToString(SystemFrequencyDeviceName, SignalKind.Frequency);
            string maxFreqSignalRef = SignalReference.ToString(SystemFrequencyDeviceName, SignalKind.Analog, 1);
            string minFreqSignalRef = SignalReference.ToString(SystemFrequencyDeviceName, SignalKind.Analog, 2);

            Measurement avgFreqMeasurement = QueryMeasurement(avgFreqSignalRef);
            Measurement maxFreqMeasurement = QueryMeasurement(maxFreqSignalRef);
            Measurement minFreqMeasurement = QueryMeasurement(minFreqSignalRef);

            avgFreqMeasurement.PointTag        = $"{SystemFrequencyDeviceName}-AVG-FQ";
            avgFreqMeasurement.SignalReference = avgFreqSignalRef;
            avgFreqMeasurement.SignalTypeID    = freqSignalType.ID;
            avgFreqMeasurement.DeviceID        = freqDevice.ID;
            avgFreqMeasurement.HistorianID     = historianID;
            avgFreqMeasurement.Description     = "Average System Frequency";
            avgFreqMeasurement.Internal        = true;
            avgFreqMeasurement.Enabled         = true;

            maxFreqMeasurement.PointTag        = $"{SystemFrequencyDeviceName}-MAX-FQ";
            maxFreqMeasurement.SignalReference = maxFreqSignalRef;
            maxFreqMeasurement.SignalTypeID    = freqSignalType.ID;
            maxFreqMeasurement.DeviceID        = freqDevice.ID;
            maxFreqMeasurement.HistorianID     = historianID;
            maxFreqMeasurement.Description     = "Maximum System Frequency";
            maxFreqMeasurement.Internal        = true;
            maxFreqMeasurement.Enabled         = true;

            minFreqMeasurement.PointTag        = $"{SystemFrequencyDeviceName}-MIN-FQ";
            minFreqMeasurement.SignalReference = minFreqSignalRef;
            minFreqMeasurement.SignalTypeID    = freqSignalType.ID;
            minFreqMeasurement.DeviceID        = freqDevice.ID;
            minFreqMeasurement.HistorianID     = historianID;
            minFreqMeasurement.Description     = "Minimum System Frequency";
            minFreqMeasurement.Internal        = true;
            minFreqMeasurement.Enabled         = true;

            AddNewOrUpdateMeasurement(avgFreqMeasurement);
            AddNewOrUpdateMeasurement(maxFreqMeasurement);
            AddNewOrUpdateMeasurement(minFreqMeasurement);

            Measurement[] measurements = new Measurement[3];

            // Requery frequency measurements in case they were newly added - this will retrieve autoinc / new Guids values
            measurements[0] = QueryMeasurement(avgFreqSignalRef);
            measurements[1] = QueryMeasurement(maxFreqSignalRef);
            measurements[2] = QueryMeasurement(minFreqSignalRef);

            return(measurements);
        }
Beispiel #6
0
        private static void PhasorDataSourceValidation(AdoDataConnection database, string nodeIDQueryString, ulong trackingVersion, string arguments, Action<string> statusMessage, Action<Exception> processException)
        {
            // Make sure setting exists to allow user to by-pass phasor data source validation at startup
            ConfigurationFile configFile = ConfigurationFile.Current;
            CategorizedSettingsElementCollection settings = configFile.Settings["systemSettings"];
            settings.Add("ProcessPhasorDataSourceValidation", true, "Determines if the phasor data source validation should be processed at startup");

            // See if this node should process phasor source validation
            if (!settings["ProcessPhasorDataSourceValidation"].ValueAsBoolean())
                return;

            Dictionary<string, string> args = new Dictionary<string, string>();
            bool skipOptimization = false, renameAllPointTags = false;
            string arg, acronym;

            if (!string.IsNullOrEmpty(arguments))
                args = arguments.ParseKeyValuePairs();

            if (args.TryGetValue("skipOptimization", out arg))
                skipOptimization = arg.ParseBoolean();

            if (args.TryGetValue("renameAllPointTags", out arg))
                renameAllPointTags = arg.ParseBoolean();

            CreateDefaultNode(database, nodeIDQueryString, statusMessage, processException);
            LoadDefaultConfigurationEntity(database, statusMessage, processException);
            LoadDefaultInterconnection(database, statusMessage, processException);
            LoadDefaultProtocol(database, statusMessage, processException);
            LoadDefaultSignalType(database, statusMessage, processException);
            LoadDefaultStatistic(database, statusMessage, processException);
            EstablishDefaultMeasurementKeyCache(database, statusMessage, processException);

            statusMessage("Validating signal types...");

            // Validate that the acronym for status flags is FLAG (it was STAT in prior versions)
            if (database.Connection.ExecuteScalar("SELECT Acronym FROM SignalType WHERE Suffix='SF'").ToNonNullString().ToUpper() == "STAT")
                database.Connection.ExecuteNonQuery("UPDATE SignalType SET Acronym='FLAG' WHERE Suffix='SF'");

            // Validate that the calculation and statistic signal types are defined (they did not in initial release)
            if (Convert.ToInt32(database.Connection.ExecuteScalar("SELECT COUNT(*) FROM SignalType WHERE Acronym='CALC'")) == 0)
                database.Connection.ExecuteNonQuery("INSERT INTO SignalType(Name, Acronym, Suffix, Abbreviation, LongAcronym, Source, EngineeringUnits) VALUES('Calculated Value', 'CALC', 'CV', 'CV', 'Calculated', 'PMU', '')");

            if (Convert.ToInt32(database.Connection.ExecuteScalar("SELECT COUNT(*) FROM SignalType WHERE Acronym='STAT'")) == 0)
                database.Connection.ExecuteNonQuery("INSERT INTO SignalType(Name, Acronym, Suffix, Abbreviation, LongAcronym, Source, EngineeringUnits) VALUES('Statistic', 'STAT', 'ST', 'ST', 'Statistic', 'Any', '')");

            if (Convert.ToInt32(database.Connection.ExecuteScalar("SELECT COUNT(*) FROM SignalType WHERE Acronym='QUAL'")) == 0)
                database.Connection.ExecuteNonQuery("INSERT INTO SignalType(Name, Acronym, Suffix, Abbreviation, LongAcronym, Source, EngineeringUnits) VALUES('Quality Flags', 'QUAL', 'QF', 'QF', 'QualityFlags', 'Frame', '')");

            // Make sure values are defined for long acronyms (did not exist in prior versions)
            if (Convert.ToInt32(database.Connection.ExecuteScalar("SELECT COUNT(*) FROM SignalType WHERE LongAcronym='Undefined'")) > 0)
            {
                // Update abbreviations to better values for consistent custom point tag naming convention
                if (database.Connection.ExecuteScalar("SELECT Abbreviation FROM SignalType WHERE Acronym='ALOG'").ToNonNullString().ToUpper() == "A")
                    database.Connection.ExecuteNonQuery("UPDATE SignalType SET Abbreviation='AV' WHERE Acronym='ALOG'");

                if (database.Connection.ExecuteScalar("SELECT Abbreviation FROM SignalType WHERE Acronym='DIGI'").ToNonNullString().ToUpper() == "D")
                    database.Connection.ExecuteNonQuery("UPDATE SignalType SET Abbreviation='DV' WHERE Acronym='DIGI'");

                if (database.Connection.ExecuteScalar("SELECT Abbreviation FROM SignalType WHERE Acronym='CALC'").ToNonNullString().ToUpper() == "C")
                    database.Connection.ExecuteNonQuery("UPDATE SignalType SET Abbreviation='CV' WHERE Acronym='CALC'");

                if (database.Connection.ExecuteScalar("SELECT Abbreviation FROM SignalType WHERE Acronym='STAT'").ToNonNullString().ToUpper() == "P")
                    database.Connection.ExecuteNonQuery("UPDATE SignalType SET Abbreviation='ST' WHERE Acronym='STAT'");

                if (database.Connection.ExecuteScalar("SELECT Abbreviation FROM SignalType WHERE Acronym='QUAL'").ToNonNullString().ToUpper() == "Q")
                    database.Connection.ExecuteNonQuery("UPDATE SignalType SET Abbreviation='QF' WHERE Acronym='QUAL'");

                IEnumerable<DataRow> signalTypes = database.Connection.RetrieveData(database.AdapterType, "SELECT Name, Acronym FROM SignalType WHERE LongAcronym='Undefined'").AsEnumerable();
                string longAcronym;

                foreach (DataRow row in signalTypes)
                {
                    acronym = row.Field<string>("Acronym").ToUpperInvariant().Trim();

                    switch (acronym)
                    {
                        case "IPHM":
                            longAcronym = "CurrentMagnitude";
                            break;
                        case "IPHA":
                            longAcronym = "CurrentAngle";
                            break;
                        case "VPHM":
                            longAcronym = "VoltageMagnitude";
                            break;
                        case "VPHA":
                            longAcronym = "VoltageAngle";
                            break;
                        case "FREQ":
                            longAcronym = "Frequency";
                            break;
                        case "DFDT":
                            longAcronym = "DfDt";
                            break;
                        case "ALOG":
                            longAcronym = "Analog";
                            break;
                        case "FLAG":
                            longAcronym = "StatusFlags";
                            break;
                        case "DIGI":
                            longAcronym = "Digital";
                            break;
                        case "CALC":
                            longAcronym = "Calculated";
                            break;
                        case "STAT":
                            longAcronym = "Statistic";
                            break;
                        case "ALRM":
                            longAcronym = "Alarm";
                            break;
                        case "QUAL":
                            longAcronym = "QualityFlags";
                            break;
                        default:
                            longAcronym = row.Field<string>("Name").Trim().RemoveWhiteSpace();

                            if (string.IsNullOrEmpty(longAcronym))
                                longAcronym = acronym.ToNonNullString("?");

                            break;
                    }

                    database.Connection.ExecuteNonQuery(string.Format("UPDATE SignalType SET LongAcronym='{0}' WHERE Acronym='{1}'", longAcronym, acronym));
                }
            }

            statusMessage("Validating output stream device ID codes...");

            // Validate all ID codes for output stream devices are not set their default value
            database.Connection.ExecuteNonQuery("UPDATE OutputStreamDevice SET IDCode = ID WHERE IDCode = 0");

            statusMessage("Verifying statistics archive exists...");

            // Validate that the statistics historian exists
            if (Convert.ToInt32(database.Connection.ExecuteScalar(string.Format("SELECT COUNT(*) FROM Historian WHERE Acronym='STAT' AND NodeID={0}", nodeIDQueryString))) == 0)
                database.Connection.ExecuteNonQuery(string.Format("INSERT INTO Historian(NodeID, Acronym, Name, AssemblyName, TypeName, ConnectionString, IsLocal, Description, LoadOrder, Enabled) VALUES({0}, 'STAT', 'Statistics Archive', 'HistorianAdapters.dll', 'HistorianAdapters.LocalOutputAdapter', '', 1, 'Local historian used to archive system statistics', 9999, 1)", nodeIDQueryString));

            // Make sure statistics path exists to hold historian files
            string statisticsPath = FilePath.GetAbsolutePath(FilePath.AddPathSuffix("Statistics"));

            if (!Directory.Exists(statisticsPath))
                Directory.CreateDirectory(statisticsPath);

            // Make sure needed statistic historian configuration settings are properly defined
            settings = configFile.Settings["statMetadataFile"];
            settings.Add("FileName", string.Format("Statistics{0}stat_dbase.dat", Path.DirectorySeparatorChar), "Name of the statistics meta-data file including its path.");
            settings.Add("LoadOnOpen", true, "True if file records are to be loaded in memory when opened; otherwise False - this defaults to True for the statistics meta-data file.");
            settings.Add("ReloadOnModify", false, "True if file records loaded in memory are to be re-loaded when file is modified on disk; otherwise False - this defaults to False for the statistics meta-data file.");
            settings["LoadOnOpen"].Update(true);
            settings["ReloadOnModify"].Update(false);

            settings = configFile.Settings["statStateFile"];
            settings.Add("FileName", string.Format("Statistics{0}stat_startup.dat", Path.DirectorySeparatorChar), "Name of the statistics state file including its path.");
            settings.Add("AutoSaveInterval", 10000, "Interval in milliseconds at which the file records loaded in memory are to be saved automatically to disk. Use -1 to disable automatic saving - this defaults to 10,000 for the statistics state file.");
            settings.Add("LoadOnOpen", true, "True if file records are to be loaded in memory when opened; otherwise False - this defaults to True for the statistics state file.");
            settings.Add("SaveOnClose", true, "True if file records loaded in memory are to be saved to disk when file is closed; otherwise False - this defaults to True for the statistics state file.");
            settings.Add("ReloadOnModify", false, "True if file records loaded in memory are to be re-loaded when file is modified on disk; otherwise False - this defaults to False for the statistics state file.");
            settings["AutoSaveInterval"].Update(10000);
            settings["LoadOnOpen"].Update(true);
            settings["SaveOnClose"].Update(true);
            settings["ReloadOnModify"].Update(false);

            settings = configFile.Settings["statIntercomFile"];
            settings.Add("FileName", string.Format("Statistics{0}scratch.dat", Path.DirectorySeparatorChar), "Name of the statistics intercom file including its path.");
            settings.Add("AutoSaveInterval", 10000, "Interval in milliseconds at which the file records loaded in memory are to be saved automatically to disk. Use -1 to disable automatic saving - this defaults to 10,000 for the statistics intercom file.");
            settings.Add("LoadOnOpen", true, "True if file records are to be loaded in memory when opened; otherwise False - this defaults to True for the statistics intercom file.");
            settings.Add("SaveOnClose", true, "True if file records loaded in memory are to be saved to disk when file is closed; otherwise False - this defaults to True for the statistics intercom file.");
            settings.Add("ReloadOnModify", false, "True if file records loaded in memory are to be re-loaded when file is modified on disk; otherwise False - this defaults to False for the statistics intercom file.");
            settings["AutoSaveInterval"].Update(1000);
            settings["LoadOnOpen"].Update(true);
            settings["SaveOnClose"].Update(true);
            settings["ReloadOnModify"].Update(false);

            settings = configFile.Settings["statArchiveFile"];
            settings.Add("FileName", string.Format("Statistics{0}stat_archive.d", Path.DirectorySeparatorChar), "Name of the statistics working archive file including its path.");
            settings.Add("CacheWrites", true, "True if writes are to be cached for performance; otherwise False - this defaults to True for the statistics working archive file.");
            settings.Add("ConserveMemory", false, "True if attempts are to be made to conserve memory; otherwise False - this defaults to False for the statistics working archive file.");
            settings["CacheWrites"].Update(true);
            settings["ConserveMemory"].Update(false);

            settings = configFile.Settings["statMetadataService"];
            settings.Add("Endpoints", "http.rest://localhost:6051/historian", "Semicolon delimited list of URIs where the web service can be accessed - this defaults to http.rest://localhost:6051/historian for the statistics meta-data service.");

            settings = configFile.Settings["statTimeSeriesDataService"];
            settings.Add("Endpoints", "http.rest://localhost:6052/historian", "Semicolon delimited list of URIs where the web service can be accessed - this defaults to http.rest://localhost:6052/historian for the statistics time-series data service.");

            configFile.Save();

            // Get the needed statistic related IDs
            int statHistorianID = Convert.ToInt32(database.Connection.ExecuteScalar(string.Format("SELECT ID FROM Historian WHERE Acronym='STAT' AND NodeID={0}", nodeIDQueryString)));

            // Load the defined system statistics
            IEnumerable<DataRow> statistics = database.Connection.RetrieveData(database.AdapterType, "SELECT * FROM Statistic ORDER BY Source, SignalIndex").AsEnumerable();

            // Filter statistics to device, input stream and output stream types            
            IEnumerable<DataRow> deviceStatistics = statistics.Where(row => string.Compare(row.Field<string>("Source"), "Device", true) == 0).ToList();
            IEnumerable<DataRow> inputStreamStatistics = statistics.Where(row => string.Compare(row.Field<string>("Source"), "InputStream", true) == 0).ToList();

            // Define kinds of output signal that will designate a location in an output stream protocol frame - other non-mappable measurements will be removed from output stream measurements
            SignalKind[] validOutputSignalKinds = { SignalKind.Angle, SignalKind.Magnitude, SignalKind.Frequency, SignalKind.DfDt, SignalKind.Status, SignalKind.Analog, SignalKind.Digital, SignalKind.Quality };

            HashSet<int> measurementIDsToDelete = new HashSet<int>();
            SignalReference deviceSignalReference;
            string query, signalReference, pointTag, company, description, protocolIDs;
            int adapterID, deviceID, signalIndex;
            bool firstStatisticExisted;
            int? historianID;

            string[] trackedTables;
            ulong changes;

            try
            {
                // Determine the tables for which changes are tracked
                if (trackingVersion != ulong.MinValue)
                {
                    trackedTables = database.Connection.RetrieveData(database.AdapterType, "SELECT Name FROM TrackedTable").Select()
                        .Select(row => row["Name"].ToNonNullString())
                        .ToArray();
                }
                else
                {
                    trackedTables = new string[0];
                }
            }
            catch
            {
                trackedTables = new string[0];
            }

            statusMessage("Validating device protocols...");

            // Extract IDs for phasor protocols
            StringBuilder protocolIDList = new StringBuilder();
            DataTable protocols = database.Connection.RetrieveData(database.AdapterType, "SELECT * FROM Protocol");

            if (protocols.Columns.Contains("Category"))
            {
                // Make sure new protocol types exist
                if (Convert.ToInt32(database.Connection.ExecuteScalar(string.Format("SELECT COUNT(*) FROM Protocol WHERE Acronym='GatewayTransport'"))) == 0)
                {
                    database.Connection.ExecuteNonQuery("INSERT INTO Protocol(Acronym, Name, Type, Category, AssemblyName, TypeName, LoadOrder) VALUES('GatewayTransport', 'Gateway Transport', 'Measurement', 'Gateway', 'GSF.TimeSeries.dll', 'GSF.TimeSeries.Transport.DataSubscriber', " + (protocols.Rows.Count + 1) + ")");

                    if (Convert.ToInt32(database.Connection.ExecuteScalar(string.Format("SELECT COUNT(*) FROM Protocol WHERE Acronym='WAV'"))) == 0)
                        database.Connection.ExecuteNonQuery("INSERT INTO Protocol(Acronym, Name, Type, Category, AssemblyName, TypeName, LoadOrder) VALUES('WAV', 'Wave Form Input Adapter', 'Frame', 'Audio', 'WavInputAdapter.dll', 'WavInputAdapter.WavInputAdapter', " + (protocols.Rows.Count + 2) + ")");

                    if (Convert.ToInt32(database.Connection.ExecuteScalar(string.Format("SELECT COUNT(*) FROM Protocol WHERE Acronym='IeeeC37_118V2'"))) == 0)
                        database.Connection.ExecuteNonQuery("INSERT INTO Protocol(Acronym, Name, Type, Category, AssemblyName, TypeName, LoadOrder) VALUES('IeeeC37_118V2', 'IEEE C37.118.2-2011', 'Frame', 'Phasor', 'PhasorProtocolAdapters.dll', 'PhasorProtocolAdapters.PhasorMeasurementMapper', 2)");

                    if (Convert.ToInt32(database.Connection.ExecuteScalar(string.Format("SELECT COUNT(*) FROM Protocol WHERE Acronym='VirtualInput'"))) == 0)
                        database.Connection.ExecuteNonQuery("INSERT INTO Protocol(Acronym, Name, Type, Category, AssemblyName, TypeName, LoadOrder) VALUES('VirtualInput', 'Virtual Device', 'Frame', 'Virtual', 'TestingAdapters.dll', 'TestingAdapters.VirtualInputAdapter', " + (protocols.Rows.Count + 4) + ")");
                }

                if (Convert.ToInt32(database.Connection.ExecuteScalar(string.Format("SELECT COUNT(*) FROM Protocol WHERE Acronym='Iec61850_90_5'"))) == 0)
                    database.Connection.ExecuteNonQuery("INSERT INTO Protocol(Acronym, Name, Type, Category, AssemblyName, TypeName, LoadOrder) VALUES('Iec61850_90_5', 'IEC 61850-90-5', 'Frame', 'Phasor', 'PhasorProtocolAdapters.dll', 'PhasorProtocolAdapters.PhasorMeasurementMapper', 12)");

                foreach (DataRow protocol in protocols.Rows)
                {
                    if (string.Compare(protocol.Field<string>("Category"), "Phasor", true) == 0)
                    {
                        if (protocolIDList.Length > 0)
                            protocolIDList.Append(", ");

                        protocolIDList.Append(protocol.ConvertField<int>("ID"));
                    }
                }
            }
            else
            {
                // Older schemas do not include protocol categories and assembly info
                foreach (DataRow protocol in protocols.Rows)
                {
                    if (protocolIDList.Length > 0)
                        protocolIDList.Append(", ");

                    protocolIDList.Append(protocol.ConvertField<int>("ID"));
                }
            }

            protocolIDs = protocolIDList.ToString();

            try
            {
                // Determine how many changes were made to devices and measurements -
                // if no changes were made, we can skip the next few steps
                if (trackedTables.Contains("Device") && trackedTables.Contains("Measurement"))
                    changes = Convert.ToUInt64(database.Connection.ExecuteScalar(string.Format("SELECT COUNT(*) FROM TrackedChange WHERE (TableName = 'Device' OR TableName = 'Measurement') AND ID > {0}", trackingVersion)));
                else
                    changes = ulong.MaxValue;
            }
            catch
            {
                changes = ulong.MaxValue;
            }

            if (skipOptimization || changes != 0L)
            {
                statusMessage("Validating device measurements...");

                // Get protocol ID list for those protocols that support time quality flags
                DataTable timeQualityProtocols = database.Connection.RetrieveData(database.AdapterType, "SELECT ID FROM Protocol WHERE Acronym = 'IeeeC37_118V1' OR Acronym = 'IeeeC37_118V2' OR Acronym = 'IeeeC37_118D6' OR Acronym = 'Iec61850_90_5'");
                StringBuilder timeQualityProtocolIDList = new StringBuilder();
                string timeQualityProtocolIDs;

                foreach (DataRow timeQualityProtocol in timeQualityProtocols.Rows)
                {
                    if (timeQualityProtocolIDList.Length > 0)
                        timeQualityProtocolIDList.Append(", ");

                    timeQualityProtocolIDList.Append(timeQualityProtocol.ConvertField<int>("ID"));
                }

                timeQualityProtocolIDs = timeQualityProtocolIDList.ToString();

                int qualityFlagsSignalTypeID = Convert.ToInt32(database.Connection.ExecuteScalar("SELECT ID FROM SignalType WHERE Acronym='QUAL'"));

                // Make sure one device quality flags measurement exists for each "connection" for devices that support time quality flags
                foreach (DataRow device in database.Connection.RetrieveData(database.AdapterType, string.Format("SELECT * FROM Device WHERE ((IsConcentrator = 0 AND ParentID IS NULL) OR IsConcentrator = 1) AND NodeID = {0} AND ProtocolID IN ({1})", nodeIDQueryString, timeQualityProtocolIDs)).Rows)
                {
                    deviceID = device.ConvertField<int>("ID");
                    acronym = device.Field<string>("Acronym");
                    signalReference = SignalReference.ToString(acronym, SignalKind.Quality);

                    // See if quality flags measurement exists for device
                    if (Convert.ToInt32(database.Connection.ExecuteScalar(string.Format("SELECT COUNT(*) FROM Measurement WHERE SignalReference = '{0}' AND DeviceID = {1}", signalReference, deviceID))) == 0)
                    {
                        historianID = device.ConvertNullableField<int>("HistorianID");

                        company = (string)database.Connection.ExecuteScalar(string.Format("SELECT MapAcronym FROM Company WHERE ID = {0}", device.ConvertNullableField<int>("CompanyID") ?? 0));

                        if (string.IsNullOrEmpty(company))
                            company = configFile.Settings["systemSettings"]["CompanyAcronym"].Value.TruncateRight(3);

                        pointTag = CreatePointTag(company, acronym, null, "QUAL");
                        description = string.Format("{0} Time Quality Flags", device.Field<string>("Name"));

                        query = database.ParameterizedQueryString("INSERT INTO Measurement(HistorianID, DeviceID, PointTag, SignalTypeID, PhasorSourceIndex, " +
                                                                  "SignalReference, Description, Enabled) VALUES({0}, {1}, {2}, {3}, NULL, {4}, {5}, 1)", "historianID", "deviceID", "pointTag",
                            "signalTypeID", "signalReference", "description");

                        database.Connection.ExecuteNonQuery(query, DataExtensions.DefaultTimeoutDuration, historianID.HasValue ? (object)historianID.Value : (object)DBNull.Value, deviceID, pointTag, qualityFlagsSignalTypeID, signalReference, description);
                    }
                }

                // Make sure needed device statistic measurements exist, currently statistics are only associated with phasor devices so we filter based on protocol
                foreach (DataRow device in database.Connection.RetrieveData(database.AdapterType, string.Format("SELECT * FROM Device WHERE IsConcentrator = 0 AND NodeID = {0} AND ProtocolID IN ({1})", nodeIDQueryString, protocolIDs)).Rows)
                {
                    foreach (DataRow statistic in deviceStatistics)
                    {
                        string oldAcronym;
                        string oldSignalReference;

                        signalIndex = statistic.ConvertField<int>("SignalIndex");
                        oldAcronym = device.Field<string>("Acronym");
                        acronym = oldAcronym + "!PMU";
                        oldSignalReference = SignalReference.ToString(oldAcronym, SignalKind.Statistic, signalIndex);
                        signalReference = SignalReference.ToString(acronym, SignalKind.Statistic, signalIndex);

                        // If the original format for device statistics is found in the database, update to new format
                        if (Convert.ToInt32(database.Connection.ExecuteScalar(string.Format("SELECT COUNT(*) FROM Measurement WHERE SignalReference='{0}' AND HistorianID={1}", oldSignalReference, statHistorianID))) > 0)
                            database.Connection.ExecuteNonQuery(string.Format("UPDATE Measurement SET SignalReference='{0}' WHERE SignalReference='{1}' AND HistorianID={2}", signalReference, oldSignalReference, statHistorianID));
                        else if (!skipOptimization)
                            break;
                    }
                }

                statusMessage("Validating input stream measurements...");

                // Make sure devices associated with a concentrator do not have any extraneous input stream statistic measurements - this can happen
                // when a device was once a direct connect device but now is part of a concentrator...
                foreach (DataRow inputStream in database.Connection.RetrieveData(database.AdapterType, string.Format("SELECT * FROM Device WHERE (IsConcentrator = 0 AND ParentID IS NOT NULL) AND NodeID = {0} AND ProtocolID IN ({1})", nodeIDQueryString, protocolIDs)).Rows)
                {
                    firstStatisticExisted = false;

                    foreach (DataRow statistic in inputStreamStatistics)
                    {
                        acronym = inputStream.Field<string>("Acronym") + "!IS";
                        signalIndex = statistic.ConvertField<int>("SignalIndex");
                        signalReference = SignalReference.ToString(acronym, SignalKind.Statistic, signalIndex);

                        // To reduce time required to execute these steps, only first statistic is verified to exist
                        if (!skipOptimization && !firstStatisticExisted)
                        {
                            firstStatisticExisted = (Convert.ToInt32(database.Connection.ExecuteScalar(string.Format("SELECT COUNT(*) FROM Measurement WHERE SignalReference='{0}'", signalReference))) > 0);

                            // If the first extraneous input statistic doesn't exist, we assume no others do as well
                            if (!firstStatisticExisted)
                                break;
                        }

                        // Remove extraneous input statistics
                        database.Connection.ExecuteNonQuery(string.Format("DELETE FROM Measurement WHERE SignalReference = '{0}'", signalReference));
                    }
                }
            }

            try
            {
                // Determine how many changes were made to output streams, devices, and measurements -
                // if no changes were made, we can skip the next few steps
                if (trackedTables.Contains("OutputStream") && trackedTables.Contains("OutputStreamDevice") && trackedTables.Contains("OutputStreamMeasurement") && trackedTables.Contains("Measurement"))
                    changes = Convert.ToUInt64(database.Connection.ExecuteScalar(string.Format("SELECT COUNT(*) FROM TrackedChange WHERE (TableName = 'OutputStream' OR TableName = 'OutputStreamDevice' OR TableName = 'OutputStreamMeasurement' OR TableName = 'Measurement') AND ID > {0}", trackingVersion)));
                else
                    changes = ulong.MaxValue;
            }
            catch
            {
                changes = ulong.MaxValue;
            }

            if (skipOptimization || changes != 0L)
            {
                statusMessage("Validating output stream measurements...");

                // Make sure needed output stream statistic measurements exist
                foreach (DataRow outputStream in database.Connection.RetrieveData(database.AdapterType, string.Format("SELECT * FROM OutputStream WHERE NodeID = {0}", nodeIDQueryString)).Rows)
                {
                    adapterID = outputStream.ConvertField<int>("ID");

                    // Load devices acronyms associated with this output stream
                    List<string> deviceAcronyms =
                        database.Connection.RetrieveData(database.AdapterType,
                            string.Format("SELECT Acronym FROM OutputStreamDevice WHERE AdapterID = {0} AND NodeID = {1}", adapterID, nodeIDQueryString))
                            .AsEnumerable()
                            .Select(row => row.Field<string>("Acronym"))
                            .ToList();

                    // Since measurements can be added to the output stream device itself (e.g., quality flags) - we add it as a valid mapping destination as well
                    deviceAcronyms.Add(outputStream.Field<string>("Acronym"));

                    // Sort list so binary search can be used to speed lookups
                    deviceAcronyms.Sort(StringComparer.OrdinalIgnoreCase);

                    // Validate measurements associated with this output stream
                    foreach (DataRow outputStreamMeasurement in database.Connection.RetrieveData(database.AdapterType, string.Format("SELECT * FROM OutputStreamMeasurement WHERE AdapterID = {0} AND NodeID = {1}", adapterID, nodeIDQueryString)).Rows)
                    {
                        // Parse output stream measurement signal reference
                        deviceSignalReference = new SignalReference(outputStreamMeasurement.Field<string>("SignalReference"));

                        // Validate that the signal reference is associated with one of the output stream's devices
                        if (deviceAcronyms.BinarySearch(deviceSignalReference.Acronym, StringComparer.OrdinalIgnoreCase) < 0)
                        {
                            // This measurement has a signal reference for a device that is not part of the associated output stream, so we mark it for deletion
                            measurementIDsToDelete.Add(outputStreamMeasurement.ConvertField<int>("ID"));
                        }

                        // Validate that the signal reference type is valid for an output stream
                        if (!validOutputSignalKinds.Any(validSignalKind => deviceSignalReference.Kind == validSignalKind))
                        {
                            // This measurement has a signal reference type that is not valid for an output stream, so we mark it for deletion
                            measurementIDsToDelete.Add(outputStreamMeasurement.ConvertField<int>("ID"));
                        }
                    }
                }
            }

            if (measurementIDsToDelete.Count > 0)
            {
                statusMessage(string.Format("Removing {0} unused output stream device measurements...", measurementIDsToDelete.Count));

                foreach (int measurementID in measurementIDsToDelete)
                {
                    database.Connection.ExecuteNonQuery(string.Format("DELETE FROM OutputStreamMeasurement WHERE ID = {0} AND NodeID = {1}", measurementID, nodeIDQueryString));
                }
            }

            if (renameAllPointTags)
            {
                statusMessage("Renaming all point tags...");

                string device, vendor, signalAcronym;
                char? phase;
                int? vendorDeviceID;
                SignalReference signal;

                foreach (DataRow measurement in database.Connection.RetrieveData(database.AdapterType, "SELECT SignalID, CompanyAcronym, DeviceAcronym, VendorDeviceID, SignalReference, SignalAcronym, Phase FROM MeasurementDetail WHERE SignalAcronym <> 'STAT' AND Internal <> 0 AND Subscribed = 0").Rows)
                {
                    company = measurement.ConvertField<string>("CompanyAcronym");

                    if (string.IsNullOrEmpty(company))
                        company = configFile.Settings["systemSettings"]["CompanyAcronym"].Value.TruncateRight(3);

                    device = measurement.ConvertField<string>("DeviceAcronym");

                    if ((object)device != null)
                    {
                        vendorDeviceID = measurement.ConvertNullableField<int>("VendorDeviceID");

                        if (vendorDeviceID.HasValue)
                            vendor = (string)database.Connection.ExecuteScalar("SELECT Acronym FROM Vendor WHERE ID = " + vendorDeviceID.Value);
                        else
                            vendor = null;

                        signalAcronym = measurement.ConvertField<string>("SignalAcronym");

                        try
                        {
                            signal = new SignalReference(measurement.ConvertField<string>("SignalReference"));
                            signalIndex = signal.Index;

                            if (signalIndex <= 0)
                                signalIndex = -1;
                        }
                        catch
                        {
                            signalIndex = -1;
                        }

                        phase = measurement.ConvertNullableField<char>("Phase");

                        database.Connection.ExecuteNonQuery(string.Format("UPDATE Measurement SET PointTag = '{0}' WHERE SignalID = '{1}'", CreatePointTag(company, device, vendor, signalAcronym, signalIndex, phase ?? '_'), database.Guid(measurement, "SignalID")));
                    }
                }
            }

            if (skipOptimization || renameAllPointTags)
            {
                // If skipOptimization is set to true, automatically set it back to false
                const string clearParametersQuery =
                    "UPDATE DataOperation SET Arguments = '' " +
                    "WHERE AssemblyName = 'PhasorProtocolAdapters.dll' " +
                    "AND TypeName = 'PhasorProtocolAdapters.CommonPhasorServices' " +
                    "AND MethodName = 'PhasorDataSourceValidation'";

                database.Connection.ExecuteNonQuery(clearParametersQuery);
            }
        }
Beispiel #7
0
 /// <summary>
 /// Constructs a new <see cref="SignalReferenceMeasurement"/> from the specified parameters.
 /// </summary>
 /// <param name="measurement">Source <see cref="IMeasurement"/> value.</param>
 /// <param name="signalReference">Associated <see cref="SignalReference"/>.</param>
 public SignalReferenceMeasurement(IMeasurement measurement, SignalReference signalReference)
 {
     m_measurement   = measurement;
     SignalReference = signalReference;
 }
        public void UpdateConfiguration()
        {
            const int labelLength = 16;
            Dictionary<string, int> signalCellIndexes = new Dictionary<string, int>();
            ConfigurationCell cell;
            SignalReference signal;
            SignalReference[] signals;
            MeasurementKey measurementKey;
            PhasorType phasorType;
            AnalogType analogType;
            char phaseType;
            string label;
            int order;
            uint scale;
            double offset;

            // Define a protocol independent configuration frame
            m_baseConfigurationFrame = new ConfigurationFrame(m_idCode, DateTime.UtcNow.Ticks, (ushort)base.FramesPerSecond);

            // Define configuration cells (i.e., PMU's that will appear in outgoing data stream)
            foreach (DataRow deviceRow in DataSource.Tables["OutputStreams"].Select(string.Format("StreamID={0}", ID)))
            {
                try
                {
                    // Create a new configuration cell
                    cell = new ConfigurationCell(m_baseConfigurationFrame, ushort.Parse(deviceRow["ID"].ToString()), deviceRow["IsVirtual"].ToNonNullString("false").ParseBoolean());

                    // The base class defaults to floating-point, polar values, derived classes can change
                    cell.PhasorDataFormat = DataFormat.FloatingPoint;
                    cell.PhasorCoordinateFormat = CoordinateFormat.Polar;
                    cell.FrequencyDataFormat = DataFormat.FloatingPoint;
                    cell.AnalogDataFormat = DataFormat.FloatingPoint;

                    cell.IDLabel = deviceRow["Acronym"].ToString().Trim();
                    cell.StationName = deviceRow["Name"].ToString().TruncateRight(cell.MaximumStationNameLength).Trim();

                    // Define all the phasors configured for this device
                    foreach (DataRow phasorRow in DataSource.Tables["OutputStreamPhasors"].Select(string.Format("DeviceID={0}", cell.IDCode), "Order"))
                    {
                        order = int.Parse(phasorRow["Order"].ToNonNullString("0"));
                        label = phasorRow["Label"].ToNonNullString("Phasor " + order).Trim().RemoveDuplicateWhiteSpace().TruncateRight(labelLength - 4);
                        phasorType = phasorRow["PhasorType"].ToNonNullString("V").Trim().ToUpper().StartsWith("V") ? PhasorType.Voltage : PhasorType.Current;
                        phaseType = phasorRow["PhaseType"].ToNonNullString("+").Trim().ToUpper()[0];
                        
                        cell.PhasorDefinitions.Add(
                            new PhasorDefinition(
                                cell,
                                GeneratePhasorLabel(label, phaseType, phasorType),
                                phasorType,
                                null));
                    }

                    // Add frequency definition
                    label = string.Format("{0} Freq", cell.IDLabel.TruncateRight(labelLength - 5)).Trim();
                    cell.FrequencyDefinition = new FrequencyDefinition(cell, label);
                    
                    // Optionally define all the analogs configured for this device
                    if (DataSource.Tables.Contains("OutputStreamAnalogs"))
                    {
                        foreach (DataRow analogRow in DataSource.Tables["OutputStreamAnalogs"].Select(string.Format("DeviceID={0}", cell.IDCode), "Order"))
                        {
                            order = int.Parse(analogRow["Order"].ToNonNullString("0"));
                            label = analogRow["Label"].ToNonNullString("Analog " + order).Trim().RemoveDuplicateWhiteSpace().TruncateRight(labelLength);
                            scale = uint.Parse(analogRow["Scale"].ToNonNullString("1"));
                            offset = double.Parse(analogRow["Offset"].ToNonNullString("0.0"));
                            analogType = analogRow["AnalogType"].ToNonNullString("SinglePointOnWave").ConvertToType<AnalogType>();

                            cell.AnalogDefinitions.Add(
                                new AnalogDefinition(
                                    cell,
                                    label,
                                    scale,
                                    offset,
                                    analogType));
                        }
                    }

                    // Optionally define all the digitals configured for this device
                    if (DataSource.Tables.Contains("OutputStreamDigitals"))
                    {
                        foreach (DataRow digitalRow in DataSource.Tables["OutputStreamDigitals"].Select(string.Format("DeviceID={0}", cell.IDCode), "Order"))
                        {
                            order = int.Parse(digitalRow["Order"].ToNonNullString("0"));
                            label = digitalRow["Label"].ToNonNullString("Digital " + order).Trim().RemoveDuplicateWhiteSpace().TruncateRight(labelLength);

                            cell.DigitalDefinitions.Add(
                                new DigitalDefinition(
                                    cell,
                                    label));
                        }
                    }

                    m_baseConfigurationFrame.Cells.Add(cell);
                }
                catch (Exception ex)
                {
                    OnProcessException(new InvalidOperationException(string.Format("Failed to define output stream device \"{0}\" due to exception: {1}", deviceRow["Acronym"].ToString().Trim(), ex.Message), ex));
                }
            }

            OnStatusMessage("Defined {0} output stream devices...", m_baseConfigurationFrame.Cells.Count);

            // Create a new signal reference dictionary indexed on measurement keys
            m_signalReferences = new Dictionary<MeasurementKey, SignalReference[]>();
            
            // Define measurement to signals cross reference dictionary
            foreach (DataRow measurementRow in DataSource.Tables["OutputStreamMeasurements"].Select(string.Format("StreamID={0}", ID)))
            {
                try
                {
                    // Create a new signal reference
                    signal = new SignalReference(measurementRow["SignalReference"].ToString());

                    // Lookup cell index by acronym - doing this work upfront will save a huge amount
                    // of work during primary measurement sorting
                    if (!signalCellIndexes.TryGetValue(signal.Acronym, out signal.CellIndex))
                    {
                        // We cache these indicies locally to speed up initialization as we'll be
                        // requesting them for the same devices over and over
                        signal.CellIndex = m_configurationFrame.Cells.IndexOfIDLabel(signal.Acronym);
                        signalCellIndexes.Add(signal.Acronym, signal.CellIndex);
                    }

                    // Define measurement key
                    measurementKey = new MeasurementKey(uint.Parse(measurementRow["PointID"].ToString()), measurementRow["Historian"].ToString());

                    // It is possible, but not as common, that a measurement will have multiple destinations
                    // within an outgoing data stream frame, hence the following
                    if (m_signalReferences.TryGetValue(measurementKey, out signals))
                    {
                        // Add a new signal to existing collection
                        List<SignalReference> signalList = new List<SignalReference>(signals);
                        signalList.Add(signal);
                        m_signalReferences[measurementKey] = signalList.ToArray();
                    }
                    else
                    {
                        // Add new signal to new collection
                        signals = new SignalReference[1];
                        signals[0] = signal;
                        m_signalReferences.Add(measurementKey, signals);
                    }
                }
                catch (Exception ex)
                {
                    OnProcessException(new InvalidOperationException(string.Format("Failed to associate measurement key to signal reference \"{0}\" due to exception: {1}", measurementRow["SignalReference"].ToString().Trim(), ex.Message), ex));
                }
            }

            // Assign action adapter input measurement keys
            InputMeasurementKeys = m_signalReferences.Keys.ToArray();

            // Create a new protocol specific configuration frame
            m_configurationFrame = CreateNewConfigurationFrame(m_baseConfigurationFrame);

            // Cache new protocol specific configuration frame
            CacheConfigurationFrame(m_configurationFrame);
        }
 /// <summary>
 /// Constructs a new <see cref="SignalReferenceMeasurement"/> from the specified parameters.
 /// </summary>
 /// <param name="measurement">Source <see cref="IMeasurement"/> value.</param>
 /// <param name="signalReference">Associated <see cref="SignalReference"/>.</param>
 public SignalReferenceMeasurement(IMeasurement measurement, SignalReference signalReference)
 {
     m_measurement = measurement;
     m_signalReference = signalReference;
 }
Beispiel #10
0
        public void UpdateConfiguration()
        {
            const int LabelLength = 16;

            PhasorType type;
            AnalogType analogType;
            char phase;
            string label, scale;
            uint scalingValue;
            int order;

            // Define a protocol independent configuration frame
            m_baseConfigurationFrame = new ConfigurationFrame(m_idCode, DateTime.UtcNow.Ticks, (ushort)base.FramesPerSecond);

            // Define configuration cells (i.e., PMU's that will appear in outgoing data stream)
            foreach (DataRow deviceRow in DataSource.Tables["OutputStreamDevices"].Select(string.Format("ParentID={0}", ID), "LoadOrder"))
            {
                try
                {
                    // Get device ID and ID code
                    int deviceID = int.Parse(deviceRow["ID"].ToString());
                    ushort idCode = ushort.Parse(deviceRow["IDCode"].ToString());

                    // If number was never assigned or is invalid, we fall back on unique database record ID
                    if (idCode == 0)
                        idCode = unchecked((ushort)deviceID);

                    // Create a new configuration cell
                    ConfigurationCell cell = new ConfigurationCell(m_baseConfigurationFrame, idCode);

                    // Assign user selected data and coordinate formats, derived classes can change
                    string formatString;

                    formatString = deviceRow["PhasorDataFormat"].ToNonNullString(m_dataFormat.ToString());
                    cell.PhasorDataFormat = (DataFormat)Enum.Parse(typeof(DataFormat), string.IsNullOrEmpty(formatString) ? m_dataFormat.ToString() : formatString, true);

                    formatString = deviceRow["FrequencyDataFormat"].ToNonNullString(m_dataFormat.ToString());
                    cell.FrequencyDataFormat = (DataFormat)Enum.Parse(typeof(DataFormat), string.IsNullOrEmpty(formatString) ? m_dataFormat.ToString() : formatString, true);

                    formatString = deviceRow["AnalogDataFormat"].ToNonNullString(m_dataFormat.ToString());
                    cell.AnalogDataFormat = (DataFormat)Enum.Parse(typeof(DataFormat), string.IsNullOrEmpty(formatString) ? m_dataFormat.ToString() : formatString, true);

                    formatString = deviceRow["CoordinateFormat"].ToNonNullString(m_coordinateFormat.ToString());
                    cell.PhasorCoordinateFormat = (CoordinateFormat)Enum.Parse(typeof(CoordinateFormat), string.IsNullOrEmpty(formatString) ? m_coordinateFormat.ToString() : formatString, true);

                    // Assign device identification labels
                    cell.IDLabel = deviceRow["Name"].ToString().TruncateRight(cell.IDLabelLength).Trim();
                    label = deviceRow["Acronym"].ToString().TruncateRight(cell.MaximumStationNameLength).Trim();

                    // Station name is serialized to configuration frame
                    cell.StationName = label;

                    // Define all the phasors configured for this device
                    foreach (DataRow phasorRow in DataSource.Tables["OutputStreamDevicePhasors"].Select(string.Format("OutputStreamDeviceID={0}", deviceID), "LoadOrder"))
                    {
                        order = int.Parse(phasorRow["LoadOrder"].ToNonNullString("0"));
                        label = phasorRow["Label"].ToNonNullString("Phasor " + order).Trim().TruncateRight(LabelLength);
                        type = phasorRow["Type"].ToNonNullString("V").Trim().ToUpper().StartsWith("V") ? PhasorType.Voltage : PhasorType.Current;
                        phase = phasorRow["Phase"].ToNonNullString("+").Trim().ToUpper()[0];
                        scale = phasorRow["ScalingValue"].ToNonNullString("0");

                        if (m_replaceWithSpaceChar != Char.MinValue)
                            label = label.Replace(m_replaceWithSpaceChar, ' ');

                        // Scale can be defined as a negative value in database, so check both formatting styles
                        if (!uint.TryParse(scale, out scalingValue))
                            scalingValue = unchecked((uint)int.Parse(scale));

                        // Choose stream defined default value if no scaling value was defined
                        if (scalingValue == 0)
                            scalingValue = (type == PhasorType.Voltage ? m_voltageScalingValue : m_currentScalingValue);

                        cell.PhasorDefinitions.Add(new PhasorDefinition(
                            cell,
                            GeneratePhasorLabel(label, phase, type),
                            scalingValue,
                            type,
                            null));
                    }

                    // Add frequency definition
                    label = string.Format("{0} Freq", cell.IDLabel.TruncateRight(LabelLength - 5)).Trim();
                    cell.FrequencyDefinition = new FrequencyDefinition(cell, label);

                    // Optionally define all the analogs configured for this device
                    if (DataSource.Tables.Contains("OutputStreamDeviceAnalogs"))
                    {
                        foreach (DataRow analogRow in DataSource.Tables["OutputStreamDeviceAnalogs"].Select(string.Format("OutputStreamDeviceID={0}", deviceID), "LoadOrder"))
                        {
                            order = int.Parse(analogRow["LoadOrder"].ToNonNullString("0"));
                            label = analogRow["Label"].ToNonNullString("Analog " + order).Trim().TruncateRight(LabelLength);
                            analogType = (AnalogType)int.Parse(analogRow["Type"].ToNonNullString("0"));
                            scale = analogRow["ScalingValue"].ToNonNullString("0");

                            if (m_replaceWithSpaceChar != Char.MinValue)
                                label = label.Replace(m_replaceWithSpaceChar, ' ');

                            // Scale can be defined as a negative value in database, so check both formatting styles
                            if (!uint.TryParse(scale, out scalingValue))
                                scalingValue = unchecked((uint)int.Parse(scale));

                            cell.AnalogDefinitions.Add(new AnalogDefinition(
                                cell,
                                label,
                                scalingValue == 0 ? m_analogScalingValue : scalingValue,
                                analogType));
                        }
                    }

                    // Optionally define all the digitals configured for this device
                    if (DataSource.Tables.Contains("OutputStreamDeviceDigitals"))
                    {
                        foreach (DataRow digitalRow in DataSource.Tables["OutputStreamDeviceDigitals"].Select(string.Format("OutputStreamDeviceID={0}", deviceID), "LoadOrder"))
                        {
                            order = int.Parse(digitalRow["LoadOrder"].ToNonNullString("0"));
                            scale = digitalRow["MaskValue"].ToNonNullString("0");

                            // IEEE C37.118 digital labels are defined with all 16-labels (one for each bit) in one large formatted string
                            label = digitalRow["Label"].ToNonNullString("Digital " + order).Trim().TruncateRight(LabelLength * 16);

                            if (m_replaceWithSpaceChar != Char.MinValue)
                                label = label.Replace(m_replaceWithSpaceChar, ' ');

                            // Mask can be defined as a negative value in database, so check both formatting styles
                            if (!uint.TryParse(scale, out scalingValue))
                                scalingValue = unchecked((uint)int.Parse(scale));

                            cell.DigitalDefinitions.Add(new DigitalDefinition(
                                cell,
                                label,
                                scalingValue == 0 ? m_digitalMaskValue : scalingValue));
                        }
                    }

                    m_baseConfigurationFrame.Cells.Add(cell);
                }
                catch (Exception ex)
                {
                    OnProcessException(new InvalidOperationException(string.Format("Failed to define output stream device \"{0}\" due to exception: {1}", deviceRow["Acronym"].ToString().Trim(), ex.Message), ex));
                }
            }

            OnStatusMessage("Defined {0} output stream devices...", m_baseConfigurationFrame.Cells.Count);

            // Clear any existing signal references
            m_signalReferences.Clear();

            Dictionary<string, int> signalCellIndexes = new Dictionary<string, int>();
            SignalReference signal;
            SignalReference[] signals;
            MeasurementKey measurementKey;
            bool foundQualityFlagsMeasurement = false;
            bool isQualityFlagsMeasurement;

            // Define measurement to signals cross reference dictionary
            foreach (DataRow measurementRow in DataSource.Tables["OutputStreamMeasurements"].Select(string.Format("AdapterID={0}", ID)))
            {
                isQualityFlagsMeasurement = false;

                try
                {
                    // Create a new signal reference
                    signal = new SignalReference(measurementRow["SignalReference"].ToString());

                    // See if this is the quality flags designation for this output stream 
                    if (signal.Kind == SignalKind.Quality)
                    {
                        if (Name.Equals(signal.Acronym, StringComparison.OrdinalIgnoreCase))
                        {
                            if (foundQualityFlagsMeasurement)
                                throw new Exception("Only one quality flags measurement can be assigned to an output stream - additional quality flags will be ignored.");

                            foundQualityFlagsMeasurement = true;
                            isQualityFlagsMeasurement = true;
                        }
                        else
                        {
                            throw new Exception(string.Format("Unexpected quality flags measurement assignment to \"{0}\". A single quality flags measurement can be assigned to output stream \"{1}\".", signal.Acronym, Name));
                        }
                    }
                    else
                    {
                        // Lookup cell index by acronym - doing this work upfront will save a huge amount of work during primary measurement sorting
                        if (!signalCellIndexes.TryGetValue(signal.Acronym, out signal.CellIndex))
                        {
                            // We cache these indices locally to speed up initialization as we'll be
                            // requesting them for the same devices over and over
                            signal.CellIndex = m_baseConfigurationFrame.Cells.IndexOfStationName(signal.Acronym);
                            signalCellIndexes.Add(signal.Acronym, signal.CellIndex);
                        }
                    }

                    // No need to define this measurement for sorting unless it has a destination in the outgoing frame
                    if (signal.CellIndex > -1 || isQualityFlagsMeasurement)
                    {
                        // Get historian field
                        string historian = measurementRow["Historian"].ToNonNullString();
                        string pointID = measurementRow["PointID"].ToString();

                        // Define measurement key
                        if (!string.IsNullOrEmpty(historian))
                        {
                            measurementKey = MeasurementKey.LookUpOrCreate(historian, uint.Parse(pointID));
                        }
                        else
                        {
                            DataTable activeMeasurements = DataSource.Tables["ActiveMeasurements"];
                            DataRow[] activeMeasurementRows = new DataRow[0];

                            object activeMeasurementSignalID = null;
                            object activeMeasurementID = null;

                            // OPTIMIZE: This select query will be slow on very large ActiveMeasurement implementations, consider optimization.
                            if ((object)activeMeasurements != null)
                                activeMeasurementRows = activeMeasurements.Select(string.Format("ID LIKE '*:{0}'", pointID));

                            if (activeMeasurementRows.Length == 1)
                            {
                                activeMeasurementSignalID = activeMeasurementRows[0]["SignalID"];
                                activeMeasurementID = activeMeasurementRows[0]["ID"];
                            }

                            // If we still can't find the measurement key, now is the time to give up
                            if ((object)activeMeasurementSignalID == null && (object)activeMeasurementID == null)
                                throw new Exception(string.Format("Cannot find measurement key for measurement with pointID {0}", pointID));

                            measurementKey = MeasurementKey.LookUpOrCreate(Guid.Parse(activeMeasurementRows[0]["SignalID"].ToString()), activeMeasurementID.ToString());
                        }

                        // It is possible, but not as common, that a single measurement will have multiple destinations
                        // within an outgoing data stream frame, hence the following
                        signals = m_signalReferences.GetOrAdd(measurementKey, null as SignalReference[]);

                        if ((object)signals == null)
                        {
                            // Add new signal to new collection
                            signals = new SignalReference[1];
                            signals[0] = signal;
                        }
                        else
                        {
                            // Add a new signal to existing collection
                            List<SignalReference> signalList = new List<SignalReference>(signals);
                            signalList.Add(signal);
                            signals = signalList.ToArray();
                        }

                        m_signalReferences[measurementKey] = signals;
                    }
                }
                catch (Exception ex)
                {
                    OnProcessException(new InvalidOperationException(string.Format("Failed to associate measurement key to signal reference \"{0}\" due to exception: {1}", measurementRow["SignalReference"].ToNonNullString(), ex.Message), ex));
                }
            }

            // Assign action adapter input measurement keys - this assigns the expected measurements per frame needed
            // by the concentration engine for preemptive publication 
            InputMeasurementKeys = m_signalReferences.Keys.ToArray();

            // Allow for spaces in output stream device names if a replacement character has been defined for spaces
            if (m_replaceWithSpaceChar != Char.MinValue)
            {
                foreach (IConfigurationCell cell in m_baseConfigurationFrame.Cells)
                {
                    cell.StationName = cell.StationName.Replace(m_replaceWithSpaceChar, ' ');
                }
            }

            // Create a new protocol specific configuration frame
            m_configurationFrame = CreateNewConfigurationFrame(m_baseConfigurationFrame);

            // Cache new protocol specific configuration frame
            CacheConfigurationFrame(m_configurationFrame, Name);
        }
Beispiel #11
0
 internal static global::System.Runtime.InteropServices.HandleRef getCPtr(SignalReference obj)
 {
     return((obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr);
 }