/// <summary> /// Creates a new local system cache from one that was received remotely. /// </summary> /// <param name="dataSource"><see cref="DataSet"/> based data source used to interpret local measurement keys.</param> /// <param name="remoteCache">Deserialized remote signal index cache.</param> public SignalIndexCache(DataSet dataSource, SignalIndexCache remoteCache) { m_subscriberID = remoteCache.SubscriberID; // If active measurements are defined, interpret signal cache in context of current measurement key definitions if (dataSource != null && dataSource.Tables.Contains("ActiveMeasurements")) { DataTable activeMeasurements = dataSource.Tables["ActiveMeasurements"]; m_reference = new ConcurrentDictionary <int, MeasurementKey>(); foreach (KeyValuePair <int, MeasurementKey> signalIndex in remoteCache.Reference) { Guid signalID = signalIndex.Value.SignalID; DataRow[] filteredRows = activeMeasurements.Select("SignalID = '" + signalID.ToString() + "'"); if (filteredRows.Length > 0) { DataRow row = filteredRows[0]; MeasurementKey key = MeasurementKey.LookUpOrCreate(signalID, row["ID"].ToNonNullString(MeasurementKey.Undefined.ToString())); m_reference.TryAdd(signalIndex.Key, key); } } m_unauthorizedSignalIDs = remoteCache.UnauthorizedSignalIDs; } else { // Just use remote signal index cache as-is if no local configuration exists m_reference = remoteCache.Reference; m_unauthorizedSignalIDs = remoteCache.UnauthorizedSignalIDs; } }
// Input keys can use DataSource for filtering desired set of input or output measurements // based on any table and fields in the data set by using a filter expression instead of // a list of measurement ID's. The format is as follows: // FILTER <TableName> WHERE <Expression> [ORDER BY <SortField>] // Source tables are expected to have at least the following fields: // // ID NVARCHAR Measurement key formatted as: ArchiveSource:PointID // SignalID GUID Unique identification for measurement // PointTag NVARCHAR Point tag of measurement // Adder FLOAT Adder to apply to value, if any (default to 0.0) // Multiplier FLOAT Multiplier to apply to value, if any (default to 1.0) // // Could have used standard SQL syntax here but didn't want to give user the impression // that this a standard SQL expression when it isn't - so chose the word FILTER to make // consumer was aware that this was not SQL, but SQL "like". The WHERE clause expression // uses standard SQL syntax (it is simply the DataTable.Select filter expression). /// <summary> /// Parses input measurement keys from connection string setting. /// </summary> /// <param name="dataSource">The <see cref="DataSet"/> used to define input measurement keys.</param> /// <param name="value">Value of setting used to define input measurement keys, typically "inputMeasurementKeys".</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 input measurement keys.</returns> public static MeasurementKey[] ParseInputMeasurementKeys(DataSet dataSource, string value, string measurementTable = "ActiveMeasurements") { List <MeasurementKey> keys = new List <MeasurementKey>(); MeasurementKey key; bool dataSourceAvailable = (object)dataSource != null; if (string.IsNullOrWhiteSpace(value)) { return(keys.ToArray()); } value = value.Trim(); if (dataSourceAvailable && ParseFilterExpression(value, out string tableName, out string expression, out string sortField, out int takeCount)) { foreach (DataRow row in dataSource.Tables[tableName].Select(expression, sortField).Take(takeCount)) { key = MeasurementKey.LookUpOrCreate(row["SignalID"].ToNonNullString(Guid.Empty.ToString()).ConvertToType <Guid>(), row["ID"].ToString()); if (key != MeasurementKey.Undefined) { keys.Add(key); } } }