public void Dispose()
 {
     if (!m_disposed)
     {
         try
         {
             lock (FaultSummaryIDLock)
             {
                 foreach (MappingNode node in m_processedMappingNodes)
                 {
                     FaultSummaryIDs.Remove(node.Fault.ID);
                 }
             }
         }
         finally
         {
             m_disposed = true;
         }
     }
 }
        public override void Execute(MeterDataSet meterDataSet)
        {
            MeterInfoDataContext meterInfo;
            DoubleEndedFaultDistanceTableAdapter   doubleEndedFaultDistanceAdapter;
            List <SystemEventResource.SystemEvent> systemEvents;

            MeterData.EventDataTable systemEventTable;

            double         lineLength;
            ComplexNumber  nominalImpedance;
            List <Mapping> mappings;

            int leftEventID;
            int rightEventID;
            VICycleDataGroup leftCycleDataGroup;
            VICycleDataGroup rightCycleDataGroup;

            meterInfo = m_dbAdapterContainer.GetAdapter <MeterInfoDataContext>();
            doubleEndedFaultDistanceAdapter = m_dbAdapterContainer.GetAdapter <DoubleEndedFaultDistanceTableAdapter>();

            // Get a time range for querying each system event that contains events in this meter data set
            systemEvents = SystemEventResource.GetResource(meterDataSet, m_dbAdapterContainer).SystemEvents;

            foreach (SystemEventResource.SystemEvent systemEvent in systemEvents)
            {
                // Get the full collection of events from the database that comprise the system event that overlaps this time range
                systemEventTable = m_dbAdapterContainer.GetAdapter <EventTableAdapter>().GetSystemEvent(systemEvent.StartTime, systemEvent.EndTime, m_timeTolerance);

                foreach (IGrouping <int, MeterData.EventRow> lineGrouping in systemEventTable.GroupBy(evt => evt.LineID))
                {
                    // Make sure this line connects two known meter locations
                    if (meterInfo.MeterLocationLines.Count(mll => mll.LineID == lineGrouping.Key) != 2)
                    {
                        continue;
                    }

                    // Determine the length of the line
                    lineLength = meterInfo.Lines
                                 .Where(line => line.ID == lineGrouping.Key)
                                 .Select(line => (double?)line.Length)
                                 .FirstOrDefault() ?? double.NaN;

                    if (double.IsNaN(lineLength))
                    {
                        continue;
                    }

                    // Determine the nominal impedance of the line
                    nominalImpedance = m_dbAdapterContainer.GetAdapter <FaultLocationInfoDataContext>().LineImpedances
                                       .Where(lineImpedance => lineImpedance.LineID == lineGrouping.Key)
                                       .Select(lineImpedance => new ComplexNumber(lineImpedance.R1, lineImpedance.X1))
                                       .FirstOrDefault();

                    if (!nominalImpedance.AllAssigned)
                    {
                        continue;
                    }

                    leftEventID         = 0;
                    rightEventID        = 0;
                    leftCycleDataGroup  = null;
                    rightCycleDataGroup = null;

                    // Attempt to match faults during this system event that occurred
                    // on one end of the line with faults that occurred during this
                    // system even on the other end of the line
                    mappings = GetMappings(lineGrouping);

                    foreach (Mapping mapping in mappings)
                    {
                        if (mapping.Left.FaultType == FaultType.None || mapping.Right.FaultType == FaultType.None)
                        {
                            continue;
                        }

                        // Get the cycle data for each of the two mapped faults
                        if (mapping.Left.Fault.EventID != leftEventID)
                        {
                            leftEventID        = mapping.Left.Fault.EventID;
                            leftCycleDataGroup = GetCycleData(leftEventID);
                        }

                        if (mapping.Right.Fault.EventID != rightEventID)
                        {
                            rightEventID        = mapping.Right.Fault.EventID;
                            rightCycleDataGroup = GetCycleData(rightEventID);
                        }

                        if ((object)leftCycleDataGroup == null || (object)rightCycleDataGroup == null)
                        {
                            continue;
                        }

                        // Make sure there are no other threads calculating double-ended fault distance for this mapping,
                        // and that double-ended distance has not already been calculated and entered into the database
                        lock (FaultSummaryIDLock)
                        {
                            if (FaultSummaryIDs.Contains(mapping.Left.Fault.ID))
                            {
                                continue;
                            }

                            if (FaultSummaryIDs.Contains(mapping.Right.Fault.ID))
                            {
                                continue;
                            }

                            if (doubleEndedFaultDistanceAdapter.GetCountBy(mapping.Left.Fault.ID) > 0)
                            {
                                continue;
                            }

                            if (doubleEndedFaultDistanceAdapter.GetCountBy(mapping.Right.Fault.ID) > 0)
                            {
                                continue;
                            }

                            FaultSummaryIDs.Add(mapping.Left.Fault.ID);
                            FaultSummaryIDs.Add(mapping.Right.Fault.ID);
                        }

                        // Initialize the mappings with additional data needed for double-ended fault location
                        mapping.Left.Initialize(m_dbAdapterContainer, leftCycleDataGroup, m_systemFrequency);
                        mapping.Right.Initialize(m_dbAdapterContainer, rightCycleDataGroup, m_systemFrequency);

                        // Execute the double-ended fault location algorithm
                        ExecuteFaultLocationAlgorithm(lineLength, nominalImpedance, mapping.Left, mapping.Right);
                        ExecuteFaultLocationAlgorithm(lineLength, nominalImpedance, mapping.Right, mapping.Left);

                        // Create rows in the DoubleEndedFaultDistance table
                        CreateFaultDistanceRow(lineLength, mapping.Left, mapping.Right);
                        CreateFaultDistanceRow(lineLength, mapping.Right, mapping.Left);

                        // Add these nodes to the collection of processed mapping nodes
                        m_processedMappingNodes.Add(mapping.Left);
                        m_processedMappingNodes.Add(mapping.Right);
                    }
                }
            }

            // Create a row in the FaultCurve table for every event that now has double-ended fault distance curves
            foreach (IGrouping <int, MappingNode> grouping in m_processedMappingNodes.GroupBy(node => node.Fault.EventID))
            {
                CreateFaultCurveRow(grouping);
            }
        }