All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.zepben.evolve.services.network.tracing.phases.PhaseTrace.kt Maven / Gradle / Ivy

There is a newer version: 0.24.0rc1
Show newest version
/*
 * Copyright 2020 Zeppelin Bend Pty Ltd
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

package com.zepben.evolve.services.network.tracing.phases

import com.zepben.evolve.cim.iec61970.base.core.Terminal
import com.zepben.evolve.cim.iec61970.base.wires.SinglePhaseKind
import com.zepben.evolve.services.network.NetworkService
import com.zepben.evolve.services.network.tracing.OpenTest
import com.zepben.evolve.services.network.tracing.connectivity.ConnectivityResult
import com.zepben.evolve.services.network.tracing.feeder.DirectionSelector
import com.zepben.evolve.services.network.tracing.feeder.FeederDirection
import com.zepben.evolve.services.network.tracing.phases.PhaseStep.Companion.continueAt
import com.zepben.evolve.services.network.tracing.traversals.BasicTraversal
import com.zepben.evolve.services.network.tracing.traversals.WeightedPriorityQueue.Companion.processQueue

/**
 * A class that creates commonly used phase based traces. You can add custom step actions and stop conditions
 * to the returned traversal.
 */
object PhaseTrace {

    /**
     * @return a traversal that traces along phases in all directions through all open points.
     */
    fun newTrace(): BasicTraversal = newTrace(OpenTest.IGNORE_OPEN)

    /**
     * @return a traversal that traces along phases in all directions stopping at normally open points.
     */
    fun newNormalTrace(): BasicTraversal = newTrace(OpenTest.NORMALLY_OPEN)

    /**
     * @return a traversal that traces along phases in all directions stopping at currently open points.
     */
    fun newCurrentTrace(): BasicTraversal = newTrace(OpenTest.CURRENTLY_OPEN)

    /**
     * @return a traversal that traces along phases in a downstream direction stopping at normally open points.
     */
    fun newNormalDownstreamTrace(): BasicTraversal =
        newDownstreamTrace(OpenTest.NORMALLY_OPEN, DirectionSelector.NORMAL_DIRECTION)

    /**
     * @return a traversal that traces along phases in a downstream direction stopping at currently open points.
     */
    fun newCurrentDownstreamTrace(): BasicTraversal =
        newDownstreamTrace(OpenTest.CURRENTLY_OPEN, DirectionSelector.CURRENT_DIRECTION)

    /**
     * @return a traversal that traces along phases in an upstream direction stopping at normally open points.
     */
    fun newNormalUpstreamTrace(): BasicTraversal =
        newUpstreamTrace(OpenTest.NORMALLY_OPEN, DirectionSelector.NORMAL_DIRECTION)

    /**
     * @return a traversal that traces along phases in an upstream direction stopping at currently open points.
     */
    fun newCurrentUpstreamTrace(): BasicTraversal =
        newUpstreamTrace(OpenTest.CURRENTLY_OPEN, DirectionSelector.CURRENT_DIRECTION)

    private fun newTrace(isOpenTest: OpenTest): BasicTraversal =
        BasicTraversal(queueNext(isOpenTest), processQueue { it.phases.size }, PhaseStepTracker())

    private fun newDownstreamTrace(isOpenTest: OpenTest, activeDirection: DirectionSelector): BasicTraversal =
        BasicTraversal(queueNextDownstream(isOpenTest, activeDirection), processQueue { it.phases.size }, PhaseStepTracker())

    private fun newUpstreamTrace(isOpenTest: OpenTest, activeDirection: DirectionSelector): BasicTraversal =
        BasicTraversal(queueNextUpstream(isOpenTest, activeDirection), processQueue { it.phases.size }, PhaseStepTracker())

    private fun queueNext(openTest: OpenTest): BasicTraversal.QueueNext =
        BasicTraversal.QueueNext { phaseStep, traversal ->
            val downPhases = mutableSetOf()

            phaseStep.conductingEquipment.terminals.forEach {
                downPhases.clear()
                for (phase in phaseStep.phases) {
                    if (!openTest.isOpen(phaseStep.conductingEquipment, phase)) {
                        downPhases.add(phase)
                    }
                }

                queueConnected(traversal, it, downPhases)
            }
        }

    private fun queueNextDownstream(openTest: OpenTest, activeDirection: DirectionSelector): BasicTraversal.QueueNext =
        BasicTraversal.QueueNext { phaseStep, traversal ->
            phaseStep.conductingEquipment.terminals.forEach {
                queueConnected(traversal, it, getPhasesWithDirection(openTest, activeDirection, it, phaseStep.phases, FeederDirection.DOWNSTREAM))
            }
        }

    private fun queueConnected(traversal: BasicTraversal, terminal: Terminal, downPhases: Set) {
        if (downPhases.isNotEmpty()) {
            NetworkService.connectedTerminals(terminal, downPhases).forEach {
                tryQueue(traversal, it, it.toNominalPhases)
            }
        }
    }

    private fun queueNextUpstream(openTest: OpenTest, activeDirection: DirectionSelector): BasicTraversal.QueueNext =
        BasicTraversal.QueueNext { phaseStep, traversal ->

            phaseStep.conductingEquipment.terminals.forEach { terminal ->
                val upPhases = getPhasesWithDirection(openTest, activeDirection, terminal, phaseStep.phases, FeederDirection.UPSTREAM)
                if (upPhases.isNotEmpty()) {
                    NetworkService.connectedTerminals(terminal, upPhases).forEach { cr ->
                        // When going upstream, we only want to traverse to connected terminals that have a DOWNSTREAM direction
                        if (FeederDirection.DOWNSTREAM in activeDirection.select(cr.toTerminal).value)
                            tryQueue(traversal, cr, cr.toNominalPhases)
                    }
                }
            }
        }

    private fun tryQueue(traversal: BasicTraversal, cr: ConnectivityResult, downPhases: Collection) {
        cr.to?.let { traversal.queue.add(continueAt(it, downPhases, cr.from)) }
    }

    private fun getPhasesWithDirection(
        openTest: OpenTest,
        activeDirection: DirectionSelector,
        terminal: Terminal,
        candidatePhases: Set,
        direction: FeederDirection
    ): Set {
        val matchedPhases = mutableSetOf()

        if (direction !in activeDirection.select(terminal).value)
            return matchedPhases

        val conductingEquipment = terminal.conductingEquipment!!
        for (phase in candidatePhases) {
            if (terminal.phases.singlePhases.contains(phase) && !openTest.isOpen(conductingEquipment, phase))
                matchedPhases.add(phase)
        }

        return matchedPhases
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy