Esempio n. 1
0
        /// <summary>
        /// Parses output measurements from connection string setting.
        /// </summary>
        /// <param name="dataSource">The <see cref="DataSet"/> used to define output measurements.</param>
        /// <param name="allowSelect">Determines if database access via "SELECT" statement should be allowed for defining output measurements (see remarks about security).</param>
        /// <param name="value">Value of setting used to define output measurements, typically "outputMeasurements".</param>
        /// <param name="measurementTable">Measurement table name used to load additional meta-data; this is not used when specifying a FILTER expression.</param>
        /// <returns>User selected output measurements.</returns>
        /// <remarks>
        /// Security warning: allowing SELECT statements, i.e., setting <paramref name="allowSelect"/> to <c>true</c>, should only be allowed in cases where SQL
        /// injection would not be an issue (e.g., in places where a user can already access the database and would have nothing to gain via an injection attack).
        /// </remarks>
        public static IMeasurement[] ParseOutputMeasurements(DataSet dataSource, bool allowSelect, string value, string measurementTable = "ActiveMeasurements")
        {
            List<IMeasurement> measurements = new List<IMeasurement>();
            Measurement measurement;
            MeasurementKey key;
            Guid id;
            string tableName, expression, sortField;
            int takeCount;
            bool dataSourceAvailable = (object)dataSource != null;

            if (string.IsNullOrWhiteSpace(value))
                return measurements.ToArray();

            value = value.Trim();

            if (dataSourceAvailable && ParseFilterExpression(value, out tableName, out expression, out sortField, out takeCount))
            {
                foreach (DataRow row in dataSource.Tables[tableName].Select(expression, sortField).Take(takeCount))
                {
                    id = row["SignalID"].ToNonNullString(Guid.Empty.ToString()).ConvertToType<Guid>();

                    key = MeasurementKey.LookUpOrCreate(id, row["ID"].ToString());

                    measurement = new Measurement
                    {
                        Metadata = key.Metadata,
                    };

                    measurements.Add(measurement);
                }
            }
            else if (allowSelect && value.StartsWith("SELECT ", StringComparison.OrdinalIgnoreCase))
            {
                try
                {
                    // Load settings from the system settings category				
                    ConfigurationFile config = ConfigurationFile.Current;
                    CategorizedSettingsElementCollection settings = config.Settings["systemSettings"];
                    settings.Add("AllowSelectFilterExpresssions", false, "Determines if database backed SELECT statements should be allowed as filter expressions for defining input and output measurements for adapters.");

                    // Global configuration setting can override ability to use SELECT statements
                    if (settings["AllowSelectFilterExpresssions"].ValueAsBoolean())
                    {
                        using (AdoDataConnection database = new AdoDataConnection("systemSettings"))
                        {
                            DataTable results = database.Connection.RetrieveData(database.AdapterType, value);

                            foreach (DataRow row in results.Rows)
                            {
                                id = row["SignalID"].ToNonNullString(Guid.Empty.ToString()).ConvertToType<Guid>();

                                key = MeasurementKey.LookUpOrCreate(id, row["ID"].ToString());

                                measurement = new Measurement
                                {
                                    Metadata = key.Metadata,
                                };

                                measurements.Add(measurement);
                            }
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException("Database backed SELECT statements are disabled in the configuration.");
                    }
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException($"Could not parse output measurement definition from select statement \"{value}\": {ex.Message}", ex);
                }
            }
            else
            {
                string[] elem;
                double adder, multipler;

                foreach (string item in value.Split(';'))
                {
                    if (string.IsNullOrWhiteSpace(item))
                        continue;

                    elem = item.Trim().Split(',');

                    // Trim all tokens ahead of time
                    for (int i = 0; i < elem.Length; i++)
                        elem[i] = elem[i].Trim();

                    if (Guid.TryParse(elem[0], out id))
                    {
                        // The item was parsed as a signal ID so do a straight lookup
                        key = MeasurementKey.LookUpBySignalID(id);

                        if (key == MeasurementKey.Undefined && dataSourceAvailable && dataSource.Tables.Contains(measurementTable))
                        {
                            DataRow[] filteredRows = dataSource.Tables[measurementTable].Select($"SignalID = '{id}'");

                            if (filteredRows.Length > 0)
                                MeasurementKey.TryCreateOrUpdate(id, filteredRows[0]["ID"].ToString(), out key);
                        }
                    }
                    else if (!MeasurementKey.TryParse(elem[0], out key))
                    {
                        if (dataSourceAvailable && dataSource.Tables.Contains(measurementTable))
                        {
                            DataRow[] filteredRows;

                            // The item could not be parsed as a signal ID, but we do have a data source we can use to find the signal ID
                            filteredRows = dataSource.Tables[measurementTable].Select($"ID = '{elem[0]}'");

                            if (filteredRows.Length == 0)
                            {
                                // Point tags can have commas so we do not support specification of adders and multipliers in this case -
                                // therefore, we must do our lookup based on item (not elem) and clear elem if the lookup is successful
                                filteredRows = dataSource.Tables[measurementTable].Select($"PointTag = '{item.Trim()}'");

                                if (filteredRows.Length > 0)
                                    elem = new string[0];
                            }

                            if (filteredRows.Length > 0)
                                key = MeasurementKey.LookUpOrCreate(filteredRows[0]["SignalID"].ToNonNullString(Guid.Empty.ToString()).ConvertToType<Guid>(), filteredRows[0]["ID"].ToString());
                        }

                        // If all else fails, attempt to parse the item as a measurement key
                        if (key == MeasurementKey.Undefined)
                        {
                            if (id == Guid.Empty)
                                throw new InvalidOperationException($"Could not parse output measurement definition \"{item}\" as a filter expression, measurement key, point tag or Guid");

                            throw new InvalidOperationException($"Measurement (targeted for output) with an ID of \"{item}\" is not defined or is not enabled");
                        }
                    }

                    if (key == MeasurementKey.Undefined)
                        continue;

                    // Adder and multiplier may be optionally specified
                    if (elem.Length < 2 || !double.TryParse(elem[1], out adder))
                        adder = 0.0D;

                    if (elem.Length < 3 || !double.TryParse(elem[2], out multipler))
                        multipler = 1.0D;

                    // Create a new measurement for the provided field level information
                    measurement = new Measurement
                    {
                        Metadata = key.Metadata.ChangeAdderMultiplier(adder, multipler),
                    };

                    // Attempt to lookup other associated measurement meta-data from default measurement table, if defined
                    try
                    {
                        if (dataSourceAvailable && dataSource.Tables.Contains(measurementTable))
                        {
                            DataRow[] filteredRows = dataSource.Tables[measurementTable].Select($"ID = '{key}'");

                            if (filteredRows.Length > 0)
                            {
                                DataRow row = filteredRows[0];

                                measurement.SetTagName(row["PointTag"].ToNonNullString());

                                // Manually specified adder and multiplier take precedence, but if none were specified,
                                // then those defined in the meta-data are used instead
                                if (elem.Length < 3)
                                    measurement.SetMultiplier(double.Parse(row["Multiplier"].ToString()));

                                if (elem.Length < 2)
                                    measurement.SetAdder(double.Parse(row["Adder"].ToString()));
                            }
                        }
                    }
                    catch
                    {
                        // Errors here are not catastrophic, this simply limits the available meta-data
                        measurement.SetTagName(string.Empty);
                    }

                    measurements.Add(measurement);
                }
            }

            return measurements.ToArray();
        }