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

com.zepben.evolve.services.network.tracing.feeder.AssignToFeeders.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.feeder

import com.zepben.evolve.cim.iec61970.base.auxiliaryequipment.AuxiliaryEquipment
import com.zepben.evolve.cim.iec61970.base.core.ConductingEquipment
import com.zepben.evolve.cim.iec61970.base.core.Equipment
import com.zepben.evolve.cim.iec61970.base.core.Feeder
import com.zepben.evolve.cim.iec61970.base.core.Terminal
import com.zepben.evolve.cim.iec61970.base.wires.PowerTransformer
import com.zepben.evolve.cim.iec61970.base.wires.ProtectedSwitch
import com.zepben.evolve.services.network.NetworkService
import com.zepben.evolve.services.network.tracing.traversals.BasicTraversal

/**
 * Convenience class that provides methods for assigning HV/MV feeders on a [NetworkService].
 * Requires that a Feeder have a normalHeadTerminal with associated ConductingEquipment.
 * This class is backed by a [BasicTraversal].
 */
class AssignToFeeders {

    private val normalTraversal = AssociatedTerminalTrace.newNormalTrace()
    private val currentTraversal = AssociatedTerminalTrace.newCurrentTrace()
    private lateinit var activeFeeder: Feeder

    fun run(network: NetworkService) {
        val terminalToAuxEquipment = network.sequenceOf()
            .filter { it.terminal != null }
            .groupBy { it.terminal!!.mRID }

        val feederStartPoints = network.sequenceOf()
            .mapNotNull { it.normalHeadTerminal }
            .mapNotNull { it.conductingEquipment }
            .toSet()

        configureStepActions(normalTraversal, terminalToAuxEquipment)
        configureStepActions(currentTraversal, terminalToAuxEquipment)

        configureStopConditions(normalTraversal, feederStartPoints)
        configureStopConditions(currentTraversal, feederStartPoints)

        network.sequenceOf().forEach(::run)
    }

    private fun run(feeder: Feeder) {
        activeFeeder = feeder

        val headTerminal = feeder.normalHeadTerminal ?: return

        run(normalTraversal, headTerminal)
        run(currentTraversal, headTerminal)
    }

    private fun run(traversal: BasicTraversal, headTerminal: Terminal) {
        traversal.reset()

        traversal.tracker.visit(headTerminal)
        traversal.applyStepActions(headTerminal, false)
        AssociatedTerminalTrace.queueAssociated(traversal, headTerminal)

        traversal.run()
    }

    private fun configureStepActions(traversal: BasicTraversal, terminalToAuxEquipment: Map>) {
        traversal.clearStepActions()
        normalTraversal.addStepAction(processNormal(terminalToAuxEquipment))
        currentTraversal.addStepAction(processCurrent(terminalToAuxEquipment))
    }

    private fun configureStopConditions(traversal: BasicTraversal, feederStartPoints: Set) {
        traversal.clearStopConditions()
        traversal.addStopCondition(reachedEquipment(feederStartPoints))
        traversal.addStopCondition(reachedSubstationTransformer)
        traversal.addStopCondition(reachedLv)
    }

    private val reachedEquipment: (Set) -> (Terminal) -> Boolean = { { terminal: Terminal -> it.contains(terminal.conductingEquipment) } }

    private val reachedSubstationTransformer: (Terminal) -> Boolean = { ps: Terminal ->
        val ce = ps.conductingEquipment
        ce is PowerTransformer && ce.substations.isNotEmpty()
    }

    private val reachedLv: (Terminal) -> Boolean = { terminal ->
        terminal.conductingEquipment?.baseVoltage?.let { it.nominalVoltage < 1000 } ?: false
    }

    private fun processNormal(
        terminalToAuxEquipment: Map>
    ): (Terminal, Boolean) -> Unit =
        { terminal, isStopping ->
            process(
                terminal,
                { eq, feeder ->
                    eq.addContainer(feeder)
                    // Handle classes extending Equipment
                    when (eq) {
                        is ProtectedSwitch ->
                            eq.relayFunctions.flatMap { it.schemes }.mapNotNull { it.system }.forEach { system ->
                                system.addContainer(feeder)
                            }
                    }
                },
                { feeder, eq ->
                    feeder.addEquipment(eq)
                    when (eq) {
                        is ProtectedSwitch ->
                            eq.relayFunctions.flatMap { it.schemes }.mapNotNull { it.system }.forEach { system ->
                                feeder.addEquipment(system)
                            }
                    }
                },
                isStopping,
                terminalToAuxEquipment
            )
        }

    private fun processCurrent(
        terminalToAuxEquipment: Map>
    ): (Terminal, Boolean) -> Unit =
        { terminal, isStopping ->
            process(
                terminal,
                { eq, feeder ->
                    eq.addCurrentContainer(feeder)
                    // Handle classes extending Equipment
                    when (eq) {
                        is ProtectedSwitch ->
                            eq.relayFunctions.flatMap { it.schemes }.mapNotNull { it.system }.forEach { system ->
                                system.addCurrentContainer(feeder)
                            }
                    }
                },
                { feeder, eq ->
                    feeder.addCurrentEquipment(eq)
                    // Handle classes extending Equipment
                    when (eq) {
                        is ProtectedSwitch ->
                            eq.relayFunctions.flatMap { it.schemes }.mapNotNull { it.system }.forEach { system ->
                                feeder.addCurrentEquipment(system)
                            }
                    }
                },
                isStopping,
                terminalToAuxEquipment
            )
        }

    private fun process(
        terminal: Terminal,
        assignFeederToEquipment: (Equipment, Feeder) -> Unit,
        assignEquipmentToFeeder: (Feeder, Equipment) -> Unit,
        isStopping: Boolean,
        terminalToAuxEquipment: Map>
    ) {
        if (isStopping && (reachedLv(terminal) || reachedSubstationTransformer(terminal)))
            return

        terminalToAuxEquipment[terminal.mRID]?.forEach {
            assignFeederToEquipment(it, activeFeeder)
            assignEquipmentToFeeder(activeFeeder, it)
        }

        terminal.conductingEquipment?.let {
            assignFeederToEquipment(it, activeFeeder)
            assignEquipmentToFeeder(activeFeeder, it)
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy