private BreakerOperation GetBreakerOperation(AdoDataConnection connection, int eventID, string breakerNumber, BreakerTiming breakerTiming, PhaseTiming aPhaseTiming, PhaseTiming bPhaseTiming, PhaseTiming cPhaseTiming) { TableOperations <Phase> phaseTable = new TableOperations <Phase>(connection); TableOperations <openXDA.Model.BreakerOperationType> breakerOperationTypeTable = new TableOperations <openXDA.Model.BreakerOperationType>(connection); double maxTiming = GetMaxTiming(breakerTiming, aPhaseTiming, bPhaseTiming, cPhaseTiming); string phase = GetLatestPhase(aPhaseTiming, bPhaseTiming, cPhaseTiming); BreakerOperationType type = GetBreakerOperationType(maxTiming, breakerTiming.Speed); return(new BreakerOperation() { EventID = eventID, PhaseID = phaseTable.GetOrAdd(phase).ID, BreakerOperationTypeID = breakerOperationTypeTable.GetOrAdd(type.ToString()).ID, BreakerNumber = breakerNumber, TripCoilEnergized = breakerTiming.TimeEnergized.Time, StatusBitSet = breakerTiming.IsValid ? breakerTiming.TimeCleared.Time : breakerTiming.TimeEnergized.Time, StatusBitChatter = breakerTiming.StatusChatter, APhaseCleared = aPhaseTiming.IsValid ? aPhaseTiming.TimeCleared.Time : breakerTiming.TimeEnergized.Time, BPhaseCleared = bPhaseTiming.IsValid ? bPhaseTiming.TimeCleared.Time : breakerTiming.TimeEnergized.Time, CPhaseCleared = cPhaseTiming.IsValid ? cPhaseTiming.TimeCleared.Time : breakerTiming.TimeEnergized.Time, BreakerTiming = NotNaN(maxTiming), StatusTiming = NotNaN(breakerTiming.Timing), APhaseBreakerTiming = NotNaN(aPhaseTiming.Timing), BPhaseBreakerTiming = NotNaN(bPhaseTiming.Timing), CPhaseBreakerTiming = NotNaN(cPhaseTiming.Timing), DcOffsetDetected = (aPhaseTiming.DcOffsetDetected || bPhaseTiming.DcOffsetDetected || cPhaseTiming.DcOffsetDetected), BreakerSpeed = NotNaN(breakerTiming.Speed) }); }
private void LoadRestrikeAnalysis(MeterDataSet meterDataSet) { BreakerDataResource breakerDataResource = meterDataSet.GetResource <BreakerDataResource>(); foreach (var kvp in breakerDataResource.RestrikeLookup) { DataGroup dataGroup = kvp.Key; List <BreakerDataResource.Restrike> restrikes = kvp.Value; using (AdoDataConnection connection = meterDataSet.CreateDbConnection()) { TableOperations <Event> eventTable = new TableOperations <Event>(connection); TableOperations <Phase> phaseTable = new TableOperations <Phase>(connection); TableOperations <BreakerRestrike> breakerRestrikeTable = new TableOperations <BreakerRestrike>(connection); Event evt = eventTable.GetEvent(meterDataSet.FileGroup, dataGroup); foreach (BreakerDataResource.Restrike restrike in restrikes) { Phase phase = phaseTable.GetOrAdd(restrike.Phase.ToString()); BreakerRestrike breakerRestrike = ProcessRestrike(restrike, phase, evt, new VIDataGroup(dataGroup)); breakerRestrikeTable.AddNewRecord(breakerRestrike); } } } }
public override void Execute(MeterDataSet meterDataSet) { BreakerDataResource breakerDataResource = meterDataSet.GetResource <BreakerDataResource>(); foreach (var kvp in breakerDataResource.RestrikeLookup) { DataGroup dataGroup = kvp.Key; List <BreakerDataResource.Restrike> restrikes = kvp.Value; using (AdoDataConnection connection = meterDataSet.CreateDbConnection()) { TableOperations <Event> eventTable = new TableOperations <Event>(connection); TableOperations <Phase> phaseTable = new TableOperations <Phase>(connection); TableOperations <BreakerRestrike> breakerRestrikeTable = new TableOperations <BreakerRestrike>(connection); Event evt = eventTable.GetEvent(meterDataSet.FileGroup, dataGroup); foreach (BreakerDataResource.Restrike restrike in restrikes) { Phase phase = phaseTable.GetOrAdd(restrike.Phase.ToString()); BreakerRestrike breakerRestrike = new BreakerRestrike(); breakerRestrike.EventID = evt.ID; breakerRestrike.PhaseID = phase.ID; breakerRestrike.Sample = restrike.Sample; breakerRestrike.Timestamp = restrike.Timestamp; breakerRestrikeTable.AddNewRecord(breakerRestrike); } } } LoadGTCRestrikeData(meterDataSet); }
public void AddFieldValue(AdoDataConnection connection, string name, string value, string description = null) { TableOperations <FileGroupField> fileGroupFieldTable = new TableOperations <FileGroupField>(connection); FileGroupField fileGroupField = fileGroupFieldTable.GetOrAdd(name, description); TableOperations <FileGroupFieldValue> fileGroupFieldValueTable = new TableOperations <FileGroupFieldValue>(connection); FileGroupFieldValue fileGroupFieldValue = new FileGroupFieldValue(); fileGroupFieldValue.FileGroupID = ID; fileGroupFieldValue.FileGroupFieldID = fileGroupField.ID; fileGroupFieldValue.Value = value; fileGroupFieldValueTable.AddNewRecord(fileGroupFieldValue); }
private DbDisturbance GetDisturbanceRow(AdoDataConnection connection, Disturbance disturbance) { TableOperations <EventType> eventTypeTable = new TableOperations <EventType>(connection); EventType eventType = eventTypeTable.GetOrAdd(disturbance.EventType.ToString()); TableOperations <Phase> phaseTable = new TableOperations <Phase>(connection); Phase phase = phaseTable.GetOrAdd(disturbance.Phase.ToString()); DbDisturbance dbDisturbance = new DbDisturbance(); dbDisturbance.EventTypeID = eventType.ID; dbDisturbance.PhaseID = phase.ID; dbDisturbance.Magnitude = disturbance.Magnitude; dbDisturbance.PerUnitMagnitude = ToDbFloat(disturbance.PerUnitMagnitude); dbDisturbance.StartTime = disturbance.StartTime; dbDisturbance.EndTime = disturbance.EndTime; dbDisturbance.DurationSeconds = disturbance.DurationSeconds; dbDisturbance.DurationCycles = disturbance.GetDurationCycles(m_systemFrequency); dbDisturbance.StartIndex = disturbance.StartIndex; dbDisturbance.EndIndex = disturbance.EndIndex; return(dbDisturbance); }
// Static Methods private static Series GetSeriesInfo(Meter meter, DataGroup dataGroup, string measurementTypeName, string phaseName) { int lineID = dataGroup.Line.ID; string measurementCharacteristicName = "Instantaneous"; string seriesTypeName = "Values"; char typeDesignation = (measurementTypeName == "Current") ? 'I' : measurementTypeName[0]; string phaseDesignation = (phaseName == "RES") ? "R" : phaseName.TrimEnd('N'); string channelName = string.Concat(typeDesignation, phaseDesignation); ChannelKey channelKey = new ChannelKey(lineID, 0, channelName, measurementTypeName, measurementCharacteristicName, phaseName); SeriesKey seriesKey = new SeriesKey(channelKey, seriesTypeName); Series dbSeries = meter.Channels .SelectMany(channel => channel.Series) .FirstOrDefault(series => seriesKey.Equals(new SeriesKey(series))); if ((object)dbSeries == null) { using (AdoDataConnection connection = meter.ConnectionFactory()) { Channel dbChannel = meter.Channels .FirstOrDefault(channel => channelKey.Equals(new ChannelKey(channel))); if ((object)dbChannel == null) { TableOperations <Channel> channelTable = new TableOperations <Channel>(connection); TableOperations <MeasurementType> measurementTypeTable = new TableOperations <MeasurementType>(connection); TableOperations <MeasurementCharacteristic> measurementCharacteristicTable = new TableOperations <MeasurementCharacteristic>(connection); TableOperations <Phase> phaseTable = new TableOperations <Phase>(connection); MeasurementType measurementType = measurementTypeTable.GetOrAdd(measurementTypeName); MeasurementCharacteristic measurementCharacteristic = measurementCharacteristicTable.GetOrAdd(measurementCharacteristicName); Phase phase = phaseTable.GetOrAdd(phaseName); dbChannel = new Channel() { MeterID = meter.ID, LineID = lineID, MeasurementTypeID = measurementType.ID, MeasurementCharacteristicID = measurementCharacteristic.ID, PhaseID = phase.ID, Name = channelKey.Name, SamplesPerHour = dataGroup.SamplesPerHour, Description = string.Concat(measurementCharacteristicName, " ", measurementTypeName, " ", phaseName), Enabled = true }; channelTable.AddNewRecord(dbChannel); dbChannel.ID = connection.ExecuteScalar <int>("SELECT @@IDENTITY"); meter.Channels = null; } TableOperations <Series> seriesTable = new TableOperations <Series>(connection); TableOperations <SeriesType> seriesTypeTable = new TableOperations <SeriesType>(connection); SeriesType seriesType = seriesTypeTable.GetOrAdd(seriesTypeName); dbSeries = new Series() { ChannelID = dbChannel.ID, SeriesTypeID = seriesType.ID, SourceIndexes = string.Empty }; seriesTable.AddNewRecord(dbSeries); dbSeries.ID = connection.ExecuteScalar <int>("SELECT @@IDENTITY"); dbChannel.Series = null; dbSeries = meter.Channels .SelectMany(channel => channel.Series) .First(series => seriesKey.Equals(new SeriesKey(series))); } } return(dbSeries); }
public void SummarizeFault() { using (AdoDataConnection connection = MeterDataSet.CreateDbConnection()) { TableOperations <Event> eventTable = new TableOperations <Event>(connection); TableOperations <SegmentType> segmentTypeTable = new TableOperations <SegmentType>(connection); TableOperations <openXDA.Model.FaultGroup> faultGroupTable = new TableOperations <openXDA.Model.FaultGroup>(connection); TableOperations <FaultSegment> faultSegmentTable = new TableOperations <FaultSegment>(connection); TableOperations <FaultSummary> faultSummaryTable = new TableOperations <FaultSummary>(connection); Event evt = eventTable.GetEvent(MeterDataSet.FileGroup, DataGroup); SegmentType faultSegmentType = segmentTypeTable.GetOrAdd("Fault"); // Create a fault group row for the whole group of faults if (FaultGroup.FaultDetectionLogicResult != false || FaultGroup.FaultValidationLogicResult != false) { faultGroupTable.AddNewRecord(CreateFaultGroup(evt.ID, FaultGroup)); } for (int faultIndex = 0; faultIndex < FaultGroup.Faults.Count; faultIndex++) { Fault fault = FaultGroup.Faults[faultIndex]; // Create a fault segment for the fault itself faultSegmentTable.AddNewRecord(CreateFaultSegment(evt.ID, fault, faultSegmentType)); // Create fault segments for each fault type found within the fault foreach (Fault.Segment segment in fault.Segments) { string segmentTypeName = string.Format("{0} Fault", segment.FaultType).Replace("ABC", "3-Phase"); SegmentType segmentType = segmentTypeTable.GetOrAdd(segmentTypeName); faultSegmentTable.AddNewRecord(CreateFaultSegment(evt.ID, segment, segmentType)); } // Create the fault summary rows for this fault foreach (FaultSummary faultSummary in CreateFaultSummaries(evt.ID, faultIndex + 1, fault)) { faultSummaryTable.AddNewRecord(faultSummary); } } // Generate fault curves for each algorithm used to analyze the fault TableOperations <FaultCurve> faultCurveTable = new TableOperations <FaultCurve>(connection); TableOperations <FaultCurveStatistic> faultCurveStatisticTable = new TableOperations <FaultCurveStatistic>(connection); if (FaultGroup.Faults.Any()) { for (int i = 0; i < FaultGroup.Faults[0].Curves.Count; i++) { FaultCurve faultCurve = CreateFaultCurve(evt.ID, i); faultCurveTable.AddNewRecord(faultCurve); faultCurve.ID = connection.ExecuteScalar <int>("SELECT @@IDENTITY"); for (int faultIndex = 0; faultIndex < FaultGroup.Faults.Count; faultIndex++) { Fault fault = FaultGroup.Faults[faultIndex]; if (fault.Curves[i].Series.DataPoints.Count == 0) { continue; } FaultCurveStatistic faultCurveStatistic = new FaultCurveStatistic() { FaultCurveID = faultCurve.ID, FaultNumber = faultIndex + 1, Maximum = ToDbFloat(fault.Curves[i].Maximum), Minimum = ToDbFloat(fault.Curves[i].Minimum), Average = ToDbFloat(fault.Curves[i].Average), StandardDeviation = ToDbFloat(fault.Curves[i].StandardDeviation) }; faultCurveStatisticTable.AddNewRecord(faultCurveStatistic); } } } } }
private List <Event> GetEvents(AdoDataConnection connection, MeterDataSet meterDataSet, List <DataGroup> dataGroups, List <VICycleDataGroup> viCycleDataGroups, Dictionary <DataGroup, EventClassification> eventClassifications) { int count = dataGroups .Where(dataGroup => dataGroup.Classification != DataClassification.Trend) .Where(dataGroup => dataGroup.Classification != DataClassification.Unknown) .Where(dataGroup => eventClassifications.ContainsKey(dataGroup)) .Count(); if (count == 0) { Log.Info($"No events found for file '{meterDataSet.FilePath}'."); return(new List <Event>()); } Log.Info(string.Format("Processing {0} events...", count)); List <Event> events = new List <Event>(count); TableOperations <Event> eventTable = new TableOperations <Event>(connection); TableOperations <EventType> eventTypeTable = new TableOperations <EventType>(connection); TableOperations <EventData> eventDataTable = new TableOperations <EventData>(connection); TableOperations <Incident> incidentTable = new TableOperations <Incident>(connection); for (int i = 0; i < dataGroups.Count; i++) { DataGroup dataGroup = dataGroups[i]; EventClassification eventClassification; if (dataGroup.Classification == DataClassification.Trend) { continue; } if (dataGroup.Classification == DataClassification.Unknown) { continue; } if (!eventClassifications.TryGetValue(dataGroup, out eventClassification)) { continue; } if ((object)dataGroup.Line == null && meterDataSet.Meter.MeterLocation.MeterLocationLines.Count != 1) { continue; } Line line = dataGroup.Line ?? meterDataSet.Meter.MeterLocation.MeterLocationLines.Single().Line; if (eventTable.QueryRecordCountWhere("StartTime = {0} AND EndTime = {1} AND Samples = {2} AND MeterID = {3} AND LineID = {4}", dataGroup.StartTime, dataGroup.EndTime, dataGroup.Samples, meterDataSet.Meter.ID, line.ID) > 0) { continue; } EventType eventType = eventTypeTable.GetOrAdd(eventClassification.ToString()); Incident incident = incidentTable.QueryRecordWhere("MeterID = {0} AND {1} BETWEEN StartTime AND EndTime", meterDataSet.Meter.ID, ToDateTime2(connection, dataGroup.StartTime)); Event evt = new Event() { FileGroupID = meterDataSet.FileGroup.ID, MeterID = meterDataSet.Meter.ID, LineID = line.ID, EventTypeID = eventType.ID, EventDataID = null, IncidentID = incident.ID, Name = string.Empty, StartTime = dataGroup.StartTime, EndTime = dataGroup.EndTime, Samples = dataGroup.Samples, TimeZoneOffset = (int)m_timeZone.GetUtcOffset(dataGroup.StartTime).TotalMinutes, SamplesPerSecond = 0, SamplesPerCycle = 0 }; if (dataGroup.Samples > 0) { evt.EventData = new EventData() { FileGroupID = meterDataSet.FileGroup.ID, RunTimeID = i, TimeDomainData = dataGroup.ToData(), FrequencyDomainData = viCycleDataGroups[i].ToDataGroup().ToData(), MarkedForDeletion = 0 }; evt.SamplesPerSecond = (int)Math.Round(dataGroup.SamplesPerSecond); evt.SamplesPerCycle = Transform.CalculateSamplesPerCycle(dataGroup.SamplesPerSecond, m_systemFrequency); } events.Add(evt); } Log.Info(string.Format("Finished processing {0} events.", count)); return(events); }
private void AddUndefinedChannels(MeterDataSet meterDataSet) { List <DataSeries> undefinedDataSeries = meterDataSet.DataSeries .Concat(meterDataSet.Digitals) .Where(dataSeries => (object)dataSeries.SeriesInfo.Channel.Line == null) .ToList(); if (undefinedDataSeries.Count <= 0) { return; } Meter meter = meterDataSet.Meter; if (meter.MeterLines.Count == 0) { Log.Warn($"Unable to automatically add channels to meter {meterDataSet.Meter.Name} because there are no lines associated with that meter."); return; } if (meter.MeterLines.Count > 1) { Log.Warn($"Unable to automatically add channels to meter {meterDataSet.Meter.Name} because there are too many lines associated with that meter."); return; } Line line = meter.MeterLines .Select(meterLine => meterLine.Line) .Single(); foreach (DataSeries series in undefinedDataSeries) { series.SeriesInfo.Channel.LineID = line.ID; } using (AdoDataConnection connection = meterDataSet.CreateDbConnection()) { TableOperations <MeasurementType> measurementTypeTable = new TableOperations <MeasurementType>(connection); TableOperations <MeasurementCharacteristic> measurementCharacteristicTable = new TableOperations <MeasurementCharacteristic>(connection); TableOperations <Phase> phaseTable = new TableOperations <Phase>(connection); TableOperations <SeriesType> seriesTypeTable = new TableOperations <SeriesType>(connection); Dictionary <string, MeasurementType> measurementTypeLookup = undefinedDataSeries .Select(dataSeries => dataSeries.SeriesInfo.Channel.MeasurementType) .DistinctBy(measurementType => measurementType.Name) .Select(measurementType => measurementTypeTable.GetOrAdd(measurementType.Name, measurementType.Description)) .ToDictionary(measurementType => measurementType.Name); Dictionary <string, MeasurementCharacteristic> measurementCharacteristicLookup = undefinedDataSeries .Select(dataSeries => dataSeries.SeriesInfo.Channel.MeasurementCharacteristic) .DistinctBy(measurementCharacteristic => measurementCharacteristic.Name) .Select(measurementCharacteristic => measurementCharacteristicTable.GetOrAdd(measurementCharacteristic.Name, measurementCharacteristic.Description)) .ToDictionary(measurementCharacteristic => measurementCharacteristic.Name); Dictionary <string, Phase> phaseLookup = undefinedDataSeries .Select(dataSeries => dataSeries.SeriesInfo.Channel.Phase) .DistinctBy(phase => phase.Name) .Select(phase => phaseTable.GetOrAdd(phase.Name, phase.Description)) .ToDictionary(phase => phase.Name); Dictionary <string, SeriesType> seriesTypeLookup = undefinedDataSeries .Select(dataSeries => dataSeries.SeriesInfo.SeriesType) .DistinctBy(seriesType => seriesType.Name) .Select(seriesType => seriesTypeTable.GetOrAdd(seriesType.Name, seriesType.Description)) .ToDictionary(seriesType => seriesType.Name); Dictionary <ChannelKey, Channel> channelLookup = meter.Channels .GroupBy(channel => new ChannelKey(channel)) .ToDictionary(grouping => { if (grouping.Count() > 1) { Log.Warn($"Detected duplicate channel key: {grouping.First().ID}"); } return(grouping.Key); }, grouping => grouping.First()); List <Channel> undefinedChannels = undefinedDataSeries .Select(dataSeries => dataSeries.SeriesInfo.Channel) .GroupBy(channel => new ChannelKey(channel)) .Where(grouping => !channelLookup.ContainsKey(grouping.Key)) .Select(grouping => grouping.First()) .ToList(); TableOperations <Channel> channelTable = new TableOperations <Channel>(connection); // Add all undefined channels to the database foreach (Channel channel in undefinedChannels) { string measurementTypeName = channel.MeasurementType.Name; string measurementCharacteristicName = channel.MeasurementCharacteristic.Name; string phaseName = channel.Phase.Name; channel.MeterID = meter.ID; channel.LineID = line.ID; channel.MeasurementTypeID = measurementTypeLookup[measurementTypeName].ID; channel.MeasurementCharacteristicID = measurementCharacteristicLookup[measurementCharacteristicName].ID; channel.PhaseID = phaseLookup[phaseName].ID; channel.Enabled = true; // If the per-unit value was not specified in the input file, // we can obtain the per-unit value from the line configuration // if the channel happens to be an instantaneous or RMS voltage if (!channel.PerUnitValue.HasValue) { if (IsVoltage(channel)) { if (IsLineToNeutral(channel)) { channel.PerUnitValue = (line.VoltageKV * 1000.0D) / Sqrt3; } else if (IsLineToLine(channel)) { channel.PerUnitValue = line.VoltageKV * 1000.0D; } } } channelTable.AddNewRecord(channel); } if (undefinedChannels.Count > 0) { // Refresh the channel lookup to // include all the new channels meter.Channels = null; channelLookup = meter.Channels .GroupBy(channel => new ChannelKey(channel)) .ToDictionary(grouping => grouping.Key, grouping => grouping.First()); } Dictionary <SeriesKey, Series> seriesLookup = meter.Channels .SelectMany(channel => channel.Series) .Where(series => series.SourceIndexes == "") .GroupBy(series => new SeriesKey(series)) .ToDictionary(grouping => { if (grouping.Count() > 1) { Log.Warn($"Detected duplicate series key: {grouping.First().ID}"); } return(grouping.Key); }, grouping => grouping.First()); List <Series> undefinedSeries = undefinedDataSeries .SelectMany(dataSeries => dataSeries.SeriesInfo.Channel.Series) .GroupBy(series => new SeriesKey(series)) .Where(grouping => !seriesLookup.ContainsKey(grouping.Key)) .Select(grouping => grouping.First()) .ToList(); TableOperations <Series> seriesTable = new TableOperations <Series>(connection); // Add all undefined series objects to the database foreach (Series series in undefinedSeries) { ChannelKey channelKey = new ChannelKey(series.Channel); string seriesTypeName = series.SeriesType.Name; series.ChannelID = channelLookup[channelKey].ID; series.SeriesTypeID = seriesTypeLookup[seriesTypeName].ID; series.SourceIndexes = ""; seriesTable.AddNewRecord(series); } if (undefinedSeries.Count > 0) { // Refresh the series lookup to // include all the new series foreach (Channel channel in meter.Channels) { channel.Series = null; } seriesLookup = meter.Channels .SelectMany(channel => channel.Series) .GroupBy(series => new SeriesKey(series)) .ToDictionary(grouping => grouping.Key, grouping => grouping.First()); } // Update all undefined data series to reference the new database objects foreach (DataSeries dataSeries in undefinedDataSeries) { SeriesKey seriesKey = new SeriesKey(dataSeries.SeriesInfo); Series series = seriesLookup[seriesKey]; dataSeries.SeriesInfo = series; } } }
private Series GetSeriesInfo(Meter meter, ChannelKey channelKey, SeriesKey seriesKey) { Series dbSeries = meter.Channels .SelectMany(channel => channel.Series) .FirstOrDefault(series => seriesKey.Equals(new SeriesKey(series))); if ((object)dbSeries == null) { using (AdoDataConnection connection = meter.ConnectionFactory()) { Channel dbChannel = meter.Channels .FirstOrDefault(channel => channelKey.Equals(new ChannelKey(channel))); if ((object)dbChannel == null) { TableOperations <Channel> channelTable = new TableOperations <Channel>(connection); TableOperations <MeasurementType> measurementTypeTable = new TableOperations <MeasurementType>(connection); TableOperations <MeasurementCharacteristic> measurementCharacteristicTable = new TableOperations <MeasurementCharacteristic>(connection); TableOperations <Phase> phaseTable = new TableOperations <Phase>(connection); MeasurementType measurementType = measurementTypeTable.GetOrAdd(channelKey.MeasurementType); MeasurementCharacteristic measurementCharacteristic = measurementCharacteristicTable.GetOrAdd(channelKey.MeasurementCharacteristic); Phase phase = phaseTable.GetOrAdd(channelKey.Phase); dbChannel = new Channel() { MeterID = meter.ID, LineID = channelKey.LineID, MeasurementTypeID = measurementType.ID, MeasurementCharacteristicID = measurementCharacteristic.ID, PhaseID = phase.ID, Name = channelKey.Name, SamplesPerHour = 0, Description = string.Concat(channelKey.MeasurementCharacteristic, " ", channelKey.MeasurementType, " ", channelKey.Phase), Enabled = true }; channelTable.AddNewRecord(dbChannel); dbChannel.ID = connection.ExecuteScalar <int>("SELECT @@IDENTITY"); meter.Channels = null; } TableOperations <Series> seriesTable = new TableOperations <Series>(connection); TableOperations <SeriesType> seriesTypeTable = new TableOperations <SeriesType>(connection); SeriesType seriesType = seriesTypeTable.GetOrAdd(seriesKey.SeriesType); dbSeries = new Series() { ChannelID = dbChannel.ID, SeriesTypeID = seriesType.ID, SourceIndexes = string.Empty }; seriesTable.AddNewRecord(dbSeries); dbSeries.ID = connection.ExecuteScalar <int>("SELECT @@IDENTITY"); dbChannel.Series = null; dbSeries = meter.Channels .SelectMany(channel => channel.Series) .First(series => seriesKey.Equals(new SeriesKey(series))); } } return(dbSeries); }