LCOV - code coverage report
Current view: top level - src/microsim/trigger - MSOverheadWire.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 265 339 78.2 %
Date: 2024-05-07 15:28:01 Functions: 30 40 75.0 %

          Line data    Source code
       1             : /****************************************************************************/
       2             : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3             : // Copyright (C) 2002-2024 German Aerospace Center (DLR) and others.
       4             : // This program and the accompanying materials are made available under the
       5             : // terms of the Eclipse Public License 2.0 which is available at
       6             : // https://www.eclipse.org/legal/epl-2.0/
       7             : // This Source Code may also be made available under the following Secondary
       8             : // Licenses when the conditions for such availability set forth in the Eclipse
       9             : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10             : // or later which is available at
      11             : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12             : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13             : /****************************************************************************/
      14             : /// @file    MSOverheadWire.cpp
      15             : /// @author  Jakub Sevcik (RICE)
      16             : /// @author  Jan Prikryl (RICE)
      17             : /// @date    2019-12-15
      18             : ///
      19             : // Member method definitions for MSOverheadWire and MSTractionSubstation.
      20             : /****************************************************************************/
      21             : #include <config.h>
      22             : 
      23             : #include <cassert>
      24             : #include <tuple>
      25             : #include <mutex>
      26             : #include <string.h>
      27             : 
      28             : #include <utils/vehicle/SUMOVehicle.h>
      29             : #include <utils/common/ToString.h>
      30             : #include <microsim/MSVehicleType.h>
      31             : #include <microsim/MSStoppingPlace.h>
      32             : #include <microsim/MSJunction.h>
      33             : #include <microsim/MSLane.h>
      34             : #include <microsim/MSLink.h>
      35             : #include <microsim/MSNet.h>
      36             : #include <microsim/devices/MSDevice_ElecHybrid.h>
      37             : 
      38             : // due to gOverheadWireSolver
      39             : #include <microsim/MSGlobals.h>
      40             : 
      41             : // due to solving circuit as endEndOfTimestepEvents
      42             : #include <utils/common/StaticCommand.h>
      43             : #include <utils/common/WrappingCommand.h>
      44             : #include <microsim/MSEventControl.h>
      45             : 
      46             : #include <utils/traction_wire/Node.h>
      47             : #include "MSOverheadWire.h"
      48             : 
      49             : 
      50             : Command* MSTractionSubstation::myCommandForSolvingCircuit = nullptr;
      51             : static std::mutex ow_mutex;
      52             : 
      53             : // ===========================================================================
      54             : //                                                              MSOverheadWire
      55             : // ===========================================================================
      56             : 
      57          60 : MSOverheadWire::MSOverheadWire(const std::string& overheadWireSegmentID, MSLane& lane, double startPos, double endPos, bool voltageSource) :
      58          60 :     MSStoppingPlace(overheadWireSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT, std::vector<std::string>(), lane, startPos, endPos),
      59          60 :     myVoltage(0),
      60          60 :     myChargingVehicle(false),
      61          60 :     myTotalCharge(0),
      62          60 :     myChargingVehicles({}),
      63             :                    // RICE_TODO: think about some better structure storing circuit pointers below
      64          60 :                    myTractionSubstation(nullptr),
      65          60 :                    myVoltageSource(voltageSource),
      66          60 :                    myCircuitElementPos(nullptr),
      67          60 :                    myCircuitStartNodePos(nullptr),
      68         120 : myCircuitEndNodePos(nullptr) {
      69          60 :     if (getBeginLanePosition() > getEndLanePosition()) {
      70           0 :         WRITE_WARNING(toString(SUMO_TAG_OVERHEAD_WIRE_SEGMENT) + " with ID = " + getID() + " doesn't have a valid range (" + toString(getBeginLanePosition()) + " < " + toString(getEndLanePosition()) + ").");
      71             :     }
      72          60 : }
      73             : 
      74         108 : MSOverheadWire::~MSOverheadWire() {
      75          60 :     if (myTractionSubstation != nullptr) {
      76             :         Circuit* circuit = myTractionSubstation->getCircuit();
      77          42 :         if (circuit != nullptr && myCircuitElementPos != nullptr && myCircuitElementPos->getPosNode() == myCircuitStartNodePos && myCircuitElementPos->getNegNode() == myCircuitEndNodePos) {
      78          42 :             circuit->eraseElement(myCircuitElementPos);
      79          84 :             delete myCircuitElementPos;
      80          42 :             if (myCircuitEndNodePos->getElements()->size() == 0) {
      81          16 :                 circuit->eraseNode(myCircuitEndNodePos);
      82          32 :                 delete myCircuitEndNodePos;
      83             :             }
      84          42 :             if (myCircuitStartNodePos->getElements()->size() == 0) {
      85          12 :                 circuit->eraseNode(myCircuitStartNodePos);
      86          24 :                 delete myCircuitStartNodePos;
      87             :             }
      88             :         }
      89             : 
      90          42 :         if (myTractionSubstation->numberOfOverheadSegments() <= 1) {
      91           8 :             myTractionSubstation->eraseOverheadWireSegmentFromCircuit(this);
      92             :             //RICE_TODO We should "delete myTractionSubstation;" here ...
      93             :         } else {
      94          34 :             myTractionSubstation->eraseOverheadWireSegmentFromCircuit(this);
      95             :         }
      96             :     }
      97         168 : }
      98             : 
      99             : 
     100             : void
     101          46 : MSOverheadWire::addVehicle(SUMOVehicle& veh) {
     102             :     std::lock_guard<std::mutex> guard(ow_mutex);
     103          46 :     setChargingVehicle(true);
     104          46 :     myChargingVehicles.push_back(&veh);
     105          46 :     sort(myChargingVehicles.begin(), myChargingVehicles.end(), vehicle_position_sorter());
     106          46 : }
     107             : 
     108             : void
     109          28 : MSOverheadWire::eraseVehicle(SUMOVehicle& veh) {
     110             :     std::lock_guard<std::mutex> guard(ow_mutex);
     111          28 :     myChargingVehicles.erase(std::remove(myChargingVehicles.begin(), myChargingVehicles.end(), &veh), myChargingVehicles.end());
     112          28 :     if (myChargingVehicles.size() == 0) {
     113          22 :         setChargingVehicle(false);
     114             :     }
     115             :     //sort(myChargingVehicles.begin(), myChargingVehicles.end(), vehicle_position_sorter());
     116          28 : }
     117             : 
     118             : void
     119          28 : MSOverheadWire::lock() const {
     120          28 :     ow_mutex.lock();
     121          28 : }
     122             : 
     123             : void
     124          28 : MSOverheadWire::unlock() const {
     125             :     ow_mutex.unlock();
     126          28 : }
     127             : 
     128             : void
     129          22 : MSTractionSubstation::addVehicle(MSDevice_ElecHybrid* elecHybrid) {
     130          22 :     myElecHybrid.push_back(elecHybrid);
     131          22 : }
     132             : 
     133             : void
     134          22 : MSTractionSubstation::eraseVehicle(MSDevice_ElecHybrid* veh) {
     135          22 :     myElecHybrid.erase(std::remove(myElecHybrid.begin(), myElecHybrid.end(), veh), myElecHybrid.end());
     136          22 : }
     137             : 
     138             : void
     139           0 : MSTractionSubstation::writeOut() {
     140           0 :     std::cout << "substation " << getID() << " constrols segments: \n";
     141           0 :     for (std::vector<MSOverheadWire*>::iterator it = myOverheadWireSegments.begin(); it != myOverheadWireSegments.end(); ++it) {
     142           0 :         std::cout << "        " << (*it)->getOverheadWireSegmentName() << "\n";
     143             :     }
     144           0 : }
     145             : 
     146             : 
     147           0 : std::string MSOverheadWire::getOverheadWireSegmentName() {
     148           0 :     return toString(getID());
     149             : }
     150             : 
     151           0 : MSTractionSubstation::~MSTractionSubstation() {
     152           0 : }
     153             : 
     154             : Circuit*
     155        1375 : MSOverheadWire::getCircuit() const {
     156        1375 :     if (getTractionSubstation() != nullptr) {
     157        1363 :         return getTractionSubstation()->getCircuit();
     158             :     }
     159             :     return nullptr;
     160             : }
     161             : 
     162             : double
     163           0 : MSOverheadWire::getVoltage() const {
     164           0 :     return myVoltage;
     165             : }
     166             : 
     167             : void
     168           0 : MSOverheadWire::setVoltage(double voltage) {
     169           0 :     if (voltage < 0) {
     170           0 :         WRITE_WARNING("New " + toString(SUMO_ATTR_VOLTAGE) + " for " + toString(SUMO_TAG_OVERHEAD_WIRE_SEGMENT) + " with ID = " + getID() + " isn't valid (" + toString(voltage) + ").")
     171             :     } else {
     172           0 :         myVoltage = voltage;
     173             :     }
     174           0 : }
     175             : 
     176             : void
     177          68 : MSOverheadWire::setChargingVehicle(bool value) {
     178          68 :     myChargingVehicle = value;
     179          68 : }
     180             : 
     181             : 
     182             : void
     183         360 : MSTractionSubstation::setChargingVehicle(bool value) {
     184         360 :     myChargingVehicle = value;
     185         360 : }
     186             : 
     187             : bool
     188           0 : MSOverheadWire::vehicleIsInside(const double position) const {
     189           0 :     if ((position >= getBeginLanePosition()) && (position <= getEndLanePosition())) {
     190             :         return true;
     191             :     } else {
     192           0 :         return false;
     193             :     }
     194             : }
     195             : 
     196             : 
     197             : bool
     198           0 : MSOverheadWire::isCharging() const {
     199           0 :     return myChargingVehicle;
     200             : }
     201             : 
     202             : 
     203             : void
     204         816 : MSOverheadWire::addChargeValueForOutput(double WCharged, MSDevice_ElecHybrid* elecHybrid, bool ischarging) {
     205         816 :     std::string status = "charging";
     206         816 :     if (!ischarging) {
     207             :         status = "not-charging";
     208             :     }
     209             : 
     210             :     // update total charge
     211         816 :     myTotalCharge += WCharged;
     212             :     // create charge row and insert it in myChargeValues
     213             :     const std::string vehID = elecHybrid->getHolder().getID();
     214             :     if (myChargeValues.count(vehID) == 0) {
     215          46 :         myChargedVehicles.push_back(vehID);
     216             :     }
     217         816 :     Charge C(MSNet::getInstance()->getCurrentTimeStep(), elecHybrid->getHolder().getID(), elecHybrid->getHolder().getVehicleType().getID(),
     218             :              status, WCharged, elecHybrid->getActualBatteryCapacity(), elecHybrid->getMaximumBatteryCapacity(),
     219        2448 :              elecHybrid->getVoltageOfOverheadWire(), myTotalCharge);
     220         816 :     myChargeValues[vehID].push_back(C);
     221        1632 : }
     222             : 
     223             : 
     224             : void
     225          48 : MSOverheadWire::writeOverheadWireSegmentOutput(OutputDevice& output) {
     226          48 :     int chargingSteps = 0;
     227             :     std::vector<SUMOTime> chargingSteps_list;
     228          70 :     for (const auto& item : myChargeValues) {
     229         202 :         for (auto it : item.second) {
     230         180 :             if (std::find(chargingSteps_list.begin(), chargingSteps_list.end(), it.timeStep) == chargingSteps_list.end()) {
     231         180 :                 chargingSteps_list.push_back(it.timeStep);
     232             :             }
     233         180 :         }
     234             :     }
     235          48 :     chargingSteps = (int) chargingSteps_list.size();
     236          48 :     output.openTag(SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
     237          48 :     output.writeAttr(SUMO_ATTR_ID, myID);
     238          48 :     if (getTractionSubstation() != nullptr) {
     239             :         output.writeAttr(SUMO_ATTR_TRACTIONSUBSTATIONID, getTractionSubstation()->getID());
     240             :     } else {
     241             :         output.writeAttr(SUMO_ATTR_TRACTIONSUBSTATIONID, "");
     242             :     }
     243          48 :     output.writeAttr(SUMO_ATTR_TOTALENERGYCHARGED, myTotalCharge);
     244             : 
     245             :     // RICE_TODO QUESTION myChargeValues.size() vs. chargingSteps
     246             :     // myChargeValues.size() is the number of vehicles charging sometimes from this overheadwire segment during simulation
     247             :     // chargingSteps is now the sum of chargingSteps of each vehicle, but takes also into account that at the given
     248             :     // step more than one vehicle may be charged from this segment
     249             :     output.writeAttr(SUMO_ATTR_CHARGINGSTEPS, chargingSteps);
     250             :     // output.writeAttr(SUMO_ATTR_EDGE, getLane().getEdge());
     251          48 :     output.writeAttr(SUMO_ATTR_LANE, getLane().getID());
     252             : 
     253             :     // Start writing
     254          48 :     if (myChargeValues.size() > 0) {
     255          44 :         for (const std::string& vehID : myChargedVehicles) {
     256             :             int iStart = 0;
     257          22 :             const auto& chargeSteps = myChargeValues[vehID];
     258          44 :             while (iStart < (int)chargeSteps.size()) {
     259          22 :                 int iEnd = iStart + 1;
     260          22 :                 double charged = chargeSteps[iStart].WCharged;
     261         180 :                 while (iEnd < (int)chargeSteps.size() && chargeSteps[iEnd].timeStep == chargeSteps[iEnd - 1].timeStep + DELTA_T) {
     262         158 :                     charged += chargeSteps[iEnd].WCharged;
     263         158 :                     iEnd++;
     264             :                 }
     265          22 :                 writeVehicle(output, chargeSteps, iStart, iEnd, charged);
     266             :                 iStart = iEnd;
     267             :             }
     268             :         }
     269             :     }
     270             :     // close charging station tag
     271          96 :     output.closeTag();
     272          48 : }
     273             : 
     274             : 
     275             : void
     276          22 : MSOverheadWire::writeVehicle(OutputDevice& out, const std::vector<Charge>& chargeSteps, int iStart, int iEnd, double charged) {
     277          22 :     const Charge& first = chargeSteps[iStart];
     278          22 :     out.openTag(SUMO_TAG_VEHICLE);
     279          22 :     out.writeAttr(SUMO_ATTR_ID, first.vehicleID);
     280          22 :     out.writeAttr(SUMO_ATTR_TYPE, first.vehicleType);
     281             :     out.writeAttr(SUMO_ATTR_TOTALENERGYCHARGED_VEHICLE, charged);
     282          22 :     out.writeAttr(SUMO_ATTR_CHARGINGBEGIN, time2string(first.timeStep));
     283          22 :     out.writeAttr(SUMO_ATTR_CHARGINGEND, time2string(chargeSteps[iEnd - 1].timeStep));
     284          22 :     out.writeAttr(SUMO_ATTR_MAXIMUMBATTERYCAPACITY, first.maxBatteryCapacity);
     285         202 :     for (int i = iStart; i < iEnd; i++) {
     286         180 :         const Charge& c = chargeSteps[i];
     287         180 :         out.openTag(SUMO_TAG_STEP);
     288         180 :         out.writeAttr(SUMO_ATTR_TIME, time2string(c.timeStep));
     289             :         // charge values
     290         180 :         out.writeAttr(SUMO_ATTR_CHARGING_STATUS, c.status);
     291         180 :         out.writeAttr(SUMO_ATTR_ENERGYCHARGED, c.WCharged);
     292         180 :         out.writeAttr(SUMO_ATTR_PARTIALCHARGE, c.totalEnergyCharged);
     293             :         // charging values of charging station in this timestep
     294         180 :         out.writeAttr(SUMO_ATTR_VOLTAGE, c.voltage);
     295             :         // battery status of vehicle
     296         180 :         out.writeAttr(SUMO_ATTR_ACTUALBATTERYCAPACITY, c.actualBatteryCapacity);
     297             :         // close tag timestep
     298         360 :         out.closeTag();
     299             :     }
     300          22 :     out.closeTag();
     301          22 : }
     302             : 
     303             : 
     304             : // ===========================================================================
     305             : //                                                        MSTractionSubstation
     306             : // ===========================================================================
     307             : // RICE_TODO Split MSTractionSubstation and MSOverheadWire?
     308             : // Probably no as the traction substation cannot stand alone and is always
     309             : // used together with the overhead wire. It is a bit disorganised, though.
     310             : 
     311           8 : MSTractionSubstation::MSTractionSubstation(const std::string& substationId, double voltage, double currentLimit) :
     312             :     Named(substationId),
     313           8 :     myChargingVehicle(false),
     314           8 :     myElecHybridCount(0),
     315           8 :     mySubstationVoltage(voltage),
     316           8 :     myCircuit(new Circuit(currentLimit)),
     317           8 :     myTotalEnergy(0)
     318           8 : {}
     319             : 
     320             : 
     321             : 
     322             : void
     323          38 : MSTractionSubstation::addOverheadWireSegmentToCircuit(MSOverheadWire* newOverheadWireSegment) {
     324          38 :     MSLane& lane = const_cast<MSLane&>(newOverheadWireSegment->getLane());
     325          38 :     if (lane.isInternal()) {
     326           0 :         return;
     327             :     }
     328             : 
     329             :     // RICE_TODO: consider the possibility of having more segments that belong to one lane.
     330             : 
     331          38 :     myOverheadWireSegments.push_back(newOverheadWireSegment);
     332          38 :     newOverheadWireSegment->setTractionSubstation(this);
     333             : 
     334          38 :     if (MSGlobals::gOverheadWireSolver) {
     335             : #ifdef HAVE_EIGEN
     336          38 :         Circuit* circuit = newOverheadWireSegment->getCircuit();
     337             :         const std::string segmentID = newOverheadWireSegment->getID();
     338             : 
     339          76 :         if (circuit->getNode("negNode_ground") == nullptr) {
     340          16 :             circuit->addNode("negNode_ground");
     341             :         }
     342             : 
     343             :         // convention: pNode is at the beginning of the wire segment, nNode is at the end of the wire segment
     344          38 :         newOverheadWireSegment->setCircuitStartNodePos(circuit->addNode("pNode_pos_" + segmentID));
     345          76 :         newOverheadWireSegment->setCircuitEndNodePos(circuit->addNode("nNode_pos_" + segmentID));
     346             :         // RICE_TODO: to use startPos and endPos of ovhdsegment: set the length of wire here properly
     347          38 :         newOverheadWireSegment->setCircuitElementPos(
     348          76 :             circuit->addElement("pos_" + segmentID,
     349          38 :                                 (newOverheadWireSegment->getLane().getLength()) * WIRE_RESISTIVITY,
     350             :                                 newOverheadWireSegment->getCircuitStartNodePos(),
     351             :                                 newOverheadWireSegment->getCircuitEndNodePos(),
     352             :                                 Element::ElementType::RESISTOR_traction_wire));
     353             : #else
     354             :         WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
     355             : #endif
     356             :     }
     357             : 
     358             :     const MSLane* connection = nullptr;
     359          38 :     std::string ovrhdSegmentID = ""; //ID of outgoing or incoming overhead wire segment
     360             :     MSOverheadWire* ovrhdSegment = nullptr; //pointer to outgoing or incoming overhead wire segment
     361             : 
     362             :     // RICE_TODO: simplify the code, two similar code-blocks below
     363             :     // RICE_TODO: to use startPos and endPos of ovhdsegment: if endPos+EPS > newOverheadWireSegment->getLane().getLength(),
     364             :     //            and the outgoing lanes will be skipped as there is no wire at the end of the lane
     365             : 
     366             :     /* in version before SUMO 1.0.1 the function getOutgoingLanes() returning MSLane* exists,
     367             :        in new version of SUMO the funciton getOutgoingViaLanes() returning MSLane* and MSEdge* pair exists */
     368             :     // std::vector<const MSLane*> outgoing = lane.getOutgoingLanes();
     369          38 :     const std::vector<std::pair<const MSLane*, const MSEdge*> > outgoingLanesAndEdges = lane.getOutgoingViaLanes();
     370             :     std::vector<const MSLane*> neigboringInnerLanes;
     371          38 :     neigboringInnerLanes.reserve(outgoingLanesAndEdges.size());
     372          89 :     for (size_t it = 0; it < outgoingLanesAndEdges.size(); ++it) {
     373          51 :         neigboringInnerLanes.push_back(outgoingLanesAndEdges[it].first);
     374             :     }
     375             : 
     376             :     // Check if there is an overhead wire segment on the outgoing lane. If not, do nothing, otherwise find connnecting internal lanes and
     377             :     // add all lanes (this and inner) to circuit
     378          89 :     for (std::vector<const MSLane*>::iterator it = neigboringInnerLanes.begin(); it != neigboringInnerLanes.end(); ++it) {
     379         102 :         ovrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
     380             :         // If the overhead wire segment is over the outgoing (not internal) lane
     381          51 :         if (ovrhdSegmentID != "" && !(*it)->isInternal()) {
     382          25 :             ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(ovrhdSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     383             :             // If the outgoing overhead wire segment belongs to the same substation as newOverheadWireSegment
     384             :             // RICE_TODO: define what happens if the traction stations are different (overhead wire should continue over inner segments but it is unclear to which traction substation or even circuit it should be connected)
     385          25 :             if (ovrhdSegment->getTractionSubstation() == newOverheadWireSegment->getTractionSubstation()) {
     386           2 :                 connection = lane.getInternalFollowingLane(*it);
     387           2 :                 if (connection != nullptr) {
     388             :                     //is connection a forbidden lane?
     389           6 :                     if (!(ovrhdSegment->getTractionSubstation()->isForbidden(connection) ||
     390           2 :                             ovrhdSegment->getTractionSubstation()->isForbidden(lane.getInternalFollowingLane(connection)) ||
     391           2 :                             ovrhdSegment->getTractionSubstation()->isForbidden(connection->getInternalFollowingLane(*it)))) {
     392           2 :                         addOverheadWireInnerSegmentToCircuit(newOverheadWireSegment, ovrhdSegment, connection, lane.getInternalFollowingLane(connection), connection->getInternalFollowingLane(*it));
     393             :                     }
     394             : 
     395             :                 } else {
     396           0 :                     if (MSGlobals::gOverheadWireSolver) {
     397             : #ifdef HAVE_EIGEN
     398             :                         Node* const unusedNode = newOverheadWireSegment->getCircuitEndNodePos();
     399           0 :                         for (MSOverheadWire* const ows : myOverheadWireSegments) {
     400           0 :                             if (ows->getCircuitStartNodePos() == unusedNode) {
     401             :                                 ows->setCircuitStartNodePos(ovrhdSegment->getCircuitStartNodePos());
     402             :                             }
     403           0 :                             if (ows->getCircuitEndNodePos() == unusedNode) {
     404             :                                 ows->setCircuitEndNodePos(ovrhdSegment->getCircuitStartNodePos());
     405             :                             }
     406             :                         }
     407           0 :                         newOverheadWireSegment->getCircuit()->replaceAndDeleteNode(unusedNode, ovrhdSegment->getCircuitStartNodePos());
     408             : #else
     409             :                         WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
     410             : #endif
     411             :                     }
     412             :                 }
     413             :             }
     414             :         }
     415             :     }
     416             : 
     417             :     // RICE_TODO: to use startPos and endPos of ovhdsegment: if startPos-EPS < 0,
     418             :     //            and the incoming lanes will be skipped as there is no wire at the beginning of the lane
     419             : 
     420             :     // This is the same as above, only this time checking the wires on some incoming lanes. If some of them
     421             :     // has an overhead wire segment, find the connnecting internal lanes and add all lanes (the internal
     422             :     // and this) to the circuit, otherwise do nothing.
     423          76 :     neigboringInnerLanes = lane.getNormalIncomingLanes();
     424         111 :     for (std::vector<const MSLane*>::iterator it = neigboringInnerLanes.begin(); it != neigboringInnerLanes.end(); ++it) {
     425         146 :         ovrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, (*it)->getLength() - NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
     426             :         // If the overhead wire segment is over the incoming (not internal) lane
     427          73 :         if (ovrhdSegmentID != "" && !(*it)->isInternal()) {
     428          19 :             ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(ovrhdSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     429             :             // If the incoming overhead wire segment belongs to the same substation as newOverheadWireSegment
     430             :             // RICE_TODO: define what happens if the traction stations are different (overhead wire should continue over inner segments but it is unclear to which traction substation or even circuit it should be connected)
     431          19 :             if (ovrhdSegment->getTractionSubstation() == newOverheadWireSegment->getTractionSubstation()) {
     432          17 :                 connection = (*it)->getInternalFollowingLane(&lane);
     433          17 :                 if (connection != nullptr) {
     434             :                     //is connection a forbidden lane?
     435           6 :                     if (!(ovrhdSegment->getTractionSubstation()->isForbidden(connection) ||
     436           2 :                             ovrhdSegment->getTractionSubstation()->isForbidden((*it)->getInternalFollowingLane(connection)) ||
     437           2 :                             ovrhdSegment->getTractionSubstation()->isForbidden(connection->getInternalFollowingLane(&lane)))) {
     438           2 :                         addOverheadWireInnerSegmentToCircuit(ovrhdSegment, newOverheadWireSegment, connection, (*it)->getInternalFollowingLane(connection), connection->getInternalFollowingLane(&lane));
     439             :                     }
     440             :                 } else {
     441          15 :                     if (MSGlobals::gOverheadWireSolver) {
     442             : #ifdef HAVE_EIGEN
     443             :                         Node* const unusedNode = newOverheadWireSegment->getCircuitStartNodePos();
     444          60 :                         for (MSOverheadWire* const ows : myOverheadWireSegments) {
     445          45 :                             if (ows->getCircuitStartNodePos() == unusedNode) {
     446             :                                 ows->setCircuitStartNodePos(ovrhdSegment->getCircuitEndNodePos());
     447             :                             }
     448          45 :                             if (ows->getCircuitEndNodePos() == unusedNode) {
     449             :                                 ows->setCircuitEndNodePos(ovrhdSegment->getCircuitEndNodePos());
     450             :                             }
     451             :                         }
     452          15 :                         newOverheadWireSegment->getCircuit()->replaceAndDeleteNode(unusedNode, ovrhdSegment->getCircuitEndNodePos());
     453             : #else
     454             :                         WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
     455             : #endif
     456             :                     }
     457             :                 }
     458             :             }
     459             :         }
     460             :     }
     461             : 
     462          38 :     if (MSGlobals::gOverheadWireSolver && newOverheadWireSegment->isThereVoltageSource()) {
     463             : #ifdef HAVE_EIGEN
     464          22 :         newOverheadWireSegment->getCircuit()->addElement(
     465          22 :             "voltage_source_" + newOverheadWireSegment->getID(),
     466             :             mySubstationVoltage,
     467             :             newOverheadWireSegment->getCircuitStartNodePos(),
     468             :             newOverheadWireSegment->getCircuit()->getNode("negNode_ground"),
     469             :             Element::ElementType::VOLTAGE_SOURCE_traction_wire);
     470             : #else
     471             :         WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
     472             : #endif
     473             :     }
     474             : }
     475             : 
     476             : 
     477             : void
     478           4 : MSTractionSubstation::addOverheadWireInnerSegmentToCircuit(MSOverheadWire* incomingSegment, MSOverheadWire* outgoingSegment, const MSLane* connection, const MSLane* frontConnection, const MSLane* behindConnection) {
     479           4 :     if (frontConnection == nullptr && behindConnection == nullptr) {
     480             :         // addOverheadWire from nNode of newOverheadWireSegment to pNode
     481           4 :         MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     482           4 :         myOverheadWireSegments.push_back(innerSegment);
     483           4 :         innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
     484           4 :         if (MSGlobals::gOverheadWireSolver) {
     485             : #ifdef HAVE_EIGEN
     486           8 :             Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
     487             :             innerSegment->setCircuitElementPos(elem);
     488             :             innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
     489             :             innerSegment->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
     490             : #else
     491             :             UNUSED_PARAMETER(outgoingSegment);
     492             :             WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
     493             : #endif
     494             :         }
     495           0 :     } else if (frontConnection != nullptr && behindConnection == nullptr) {
     496           0 :         MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + frontConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     497           0 :         MSOverheadWire* innerSegment2 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     498             : 
     499             :         innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
     500           0 :         myOverheadWireSegments.push_back(innerSegment);
     501             :         innerSegment2->setTractionSubstation(incomingSegment->getTractionSubstation());
     502           0 :         myOverheadWireSegments.push_back(innerSegment2);
     503             : 
     504           0 :         if (MSGlobals::gOverheadWireSolver) {
     505             : #ifdef HAVE_EIGEN
     506           0 :             Node* betweenFrontNode_pos = incomingSegment->getCircuit()->addNode("betweenFrontNode_pos_" + connection->getID());
     507           0 :             Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + frontConnection->getID(), (frontConnection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), betweenFrontNode_pos, Element::ElementType::RESISTOR_traction_wire);
     508           0 :             Element* elem2 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, betweenFrontNode_pos, outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
     509             : 
     510           0 :             innerSegment->setCircuitElementPos(elem);
     511             :             innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
     512             :             innerSegment->setCircuitEndNodePos(betweenFrontNode_pos);
     513             : 
     514           0 :             innerSegment2->setCircuitElementPos(elem2);
     515             :             innerSegment2->setCircuitStartNodePos(betweenFrontNode_pos);
     516             :             innerSegment2->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
     517             : #else
     518             :             WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
     519             : #endif
     520             :         }
     521           0 :     } else if (frontConnection == nullptr && behindConnection != nullptr) {
     522           0 :         MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     523           0 :         MSOverheadWire* innerSegment2 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + behindConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     524             : 
     525             :         innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
     526           0 :         myOverheadWireSegments.push_back(innerSegment);
     527             :         innerSegment2->setTractionSubstation(incomingSegment->getTractionSubstation());
     528           0 :         myOverheadWireSegments.push_back(innerSegment2);
     529             : 
     530           0 :         if (MSGlobals::gOverheadWireSolver) {
     531             : #ifdef HAVE_EIGEN
     532           0 :             Node* betweenBehindNode_pos = incomingSegment->getCircuit()->addNode("betweenBehindNode_pos_" + connection->getID());
     533           0 :             Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), betweenBehindNode_pos, Element::ElementType::RESISTOR_traction_wire);
     534           0 :             Element* elem2 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + behindConnection->getID(), (behindConnection->getLength()) * WIRE_RESISTIVITY, betweenBehindNode_pos, outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
     535             : 
     536           0 :             innerSegment->setCircuitElementPos(elem);
     537             :             innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
     538             :             innerSegment->setCircuitEndNodePos(betweenBehindNode_pos);
     539             : 
     540           0 :             innerSegment2->setCircuitElementPos(elem2);
     541             :             innerSegment2->setCircuitStartNodePos(betweenBehindNode_pos);
     542             :             innerSegment2->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
     543             : #else
     544             :             WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
     545             : #endif
     546             :         }
     547           0 :     } else if (frontConnection != nullptr && behindConnection != nullptr) {
     548           0 :         MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + frontConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     549           0 :         MSOverheadWire* innerSegment2 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     550           0 :         MSOverheadWire* innerSegment3 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + behindConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     551             : 
     552             :         innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
     553           0 :         myOverheadWireSegments.push_back(innerSegment);
     554             :         innerSegment2->setTractionSubstation(incomingSegment->getTractionSubstation());
     555           0 :         myOverheadWireSegments.push_back(innerSegment2);
     556             :         innerSegment3->setTractionSubstation(incomingSegment->getTractionSubstation());
     557           0 :         myOverheadWireSegments.push_back(innerSegment3);
     558             : 
     559           0 :         if (MSGlobals::gOverheadWireSolver) {
     560             : #ifdef HAVE_EIGEN
     561           0 :             Node* betweenFrontNode_pos = incomingSegment->getCircuit()->addNode("betweenFrontNode_pos_" + connection->getID());
     562           0 :             Node* betweenBehindNode_pos = incomingSegment->getCircuit()->addNode("betweenBehindNode_pos_" + connection->getID());
     563           0 :             Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + frontConnection->getID(), (frontConnection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), betweenFrontNode_pos, Element::ElementType::RESISTOR_traction_wire);
     564           0 :             Element* elem2 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, betweenFrontNode_pos, betweenBehindNode_pos, Element::ElementType::RESISTOR_traction_wire);
     565           0 :             Element* elem3 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + behindConnection->getID(), (behindConnection->getLength()) * WIRE_RESISTIVITY, betweenBehindNode_pos, outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
     566             : 
     567           0 :             innerSegment->setCircuitElementPos(elem);
     568             :             innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
     569             :             innerSegment->setCircuitEndNodePos(betweenFrontNode_pos);
     570             : 
     571           0 :             innerSegment2->setCircuitElementPos(elem2);
     572             :             innerSegment2->setCircuitStartNodePos(betweenFrontNode_pos);
     573             :             innerSegment2->setCircuitEndNodePos(betweenBehindNode_pos);
     574             : 
     575           0 :             innerSegment3->setCircuitElementPos(elem3);
     576             :             innerSegment3->setCircuitStartNodePos(betweenBehindNode_pos);
     577             :             innerSegment3->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
     578             : #else
     579             :             WRITE_WARNING(TL("Overhead circuit solver requested, but solver support not compiled in."));
     580             : #endif
     581             :         }
     582             :     }
     583           4 : }
     584             : 
     585             : 
     586          11 : void MSTractionSubstation::addOverheadWireClampToCircuit(const std::string id, MSOverheadWire* startSegment, MSOverheadWire* endSegment) {
     587          11 :     PositionVector pos_start = startSegment->getLane().getShape();
     588          11 :     PositionVector pos_end = endSegment->getLane().getShape();
     589          11 :     double distance = pos_start[0].distanceTo2D(pos_end.back());
     590             : 
     591          11 :     if (distance > 10) {
     592           4 :         WRITE_WARNING("The distance between two overhead wires during adding overhead wire clamp '" + id + "' defined for traction substation '" + startSegment->getTractionSubstation()->getID() + "' is " + toString(distance) + " m.")
     593             :     }
     594          22 :     getCircuit()->addElement(id, distance * WIRE_RESISTIVITY, startSegment->getCircuitStartNodePos(), endSegment->getCircuitEndNodePos(), Element::ElementType::RESISTOR_traction_wire);
     595          11 : }
     596             : 
     597             : 
     598             : void
     599          42 : MSTractionSubstation::eraseOverheadWireSegmentFromCircuit(MSOverheadWire* oldSegment) {
     600             :     //myOverheadWireSegments.push_back(static_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(overheadWireSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT)));
     601          42 :     myOverheadWireSegments.erase(std::remove(myOverheadWireSegments.begin(), myOverheadWireSegments.end(), oldSegment), myOverheadWireSegments.end());
     602          42 : }
     603             : 
     604             : 
     605             : bool
     606           0 : MSTractionSubstation::isCharging() const {
     607           0 :     return myChargingVehicle;
     608             : }
     609             : 
     610             : 
     611             : void
     612          22 : MSTractionSubstation::increaseElecHybridCount() {
     613          22 :     myElecHybridCount++;
     614          22 : }
     615             : 
     616             : 
     617             : void
     618          22 : MSTractionSubstation::decreaseElecHybridCount() {
     619          22 :     myElecHybridCount--;
     620          22 : }
     621             : 
     622             : 
     623           0 : void MSTractionSubstation::addForbiddenLane(MSLane* lane) {
     624           0 :     myForbiddenLanes.push_back(lane);
     625           0 : }
     626             : 
     627             : 
     628          24 : bool MSTractionSubstation::isForbidden(const MSLane* lane) {
     629          24 :     for (std::vector<MSLane*>::iterator it = myForbiddenLanes.begin(); it != myForbiddenLanes.end(); ++it) {
     630           0 :         if (lane == (*it)) {
     631             :             return true;
     632             :         }
     633             :     }
     634             :     return false;
     635             : }
     636             : 
     637             : 
     638             : void
     639          11 : MSTractionSubstation::addClamp(const std::string& id, MSOverheadWire* startPos, MSOverheadWire* endPos) {
     640          11 :     OverheadWireClamp clamp(id, startPos, endPos, false);
     641          11 :     myOverheadWireClamps.push_back(clamp);
     642          11 : }
     643             : 
     644             : 
     645             : MSTractionSubstation::OverheadWireClamp*
     646          22 : MSTractionSubstation::findClamp(std::string clampId) {
     647          46 :     for (auto it = myOverheadWireClamps.begin(); it != myOverheadWireClamps.end(); it++) {
     648          35 :         if (it->id == clampId) {
     649             :             return &(*it);
     650             :         }
     651             :     }
     652             :     return nullptr;
     653             : }
     654             : 
     655             : 
     656             : bool
     657           8 : MSTractionSubstation::isAnySectionPreviouslyDefined() {
     658           8 :     if (myOverheadWireSegments.size() > 0 || myForbiddenLanes.size() > 0 || getCircuit()->getLastId() > 0) {
     659           0 :         return true;
     660             :     }
     661             :     return false;
     662             : }
     663             : 
     664             : 
     665             : void
     666         180 : MSTractionSubstation::addSolvingCirucitToEndOfTimestepEvents() {
     667         180 :     if (!myChargingVehicle) {
     668             :         // myCommandForSolvingCircuit = new StaticCommand<MSTractionSubstation>(&MSTractionSubstation::solveCircuit);
     669         180 :         myCommandForSolvingCircuit = new WrappingCommand<MSTractionSubstation>(this, &MSTractionSubstation::solveCircuit);
     670         180 :         MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(myCommandForSolvingCircuit);
     671         180 :         setChargingVehicle(true);
     672             :     }
     673         180 : }
     674             : 
     675             : 
     676             : SUMOTime
     677         180 : MSTractionSubstation::solveCircuit(SUMOTime /*currentTime*/) {
     678             :     /*Circuit evaluation*/
     679         180 :     setChargingVehicle(false);
     680             : 
     681             : #ifdef HAVE_EIGEN
     682             : 
     683             :     // RICE_TODO: Allow for updating current limits in each time step if changed e.g. via traci or similar
     684             :     // getCircuit()->setCurrentLimit(myCurrentLimit);
     685             : 
     686             :     // Solve the electrical circuit
     687         180 :     myCircuit->solve();
     688             : 
     689         180 :     if (myCircuit->getAlphaBest() != 1.0) {
     690           0 :         WRITE_WARNINGF(TL("The requested total power could not be delivered by the overhead wire. Only % of originally requested power was provided."), toString(myCircuit->getAlphaBest()));
     691             :     }
     692             : #endif
     693             : 
     694             :     // RICE_TODO: verify what happens if eigen is not defined?
     695             :     // Note: addSolvingCirucitToEndOfTimestepEvents() and thus solveCircuit() should be called from notifyMove only if eigen is defined.
     696         180 :     addChargeValueForOutput(WATT2WATTHR(myCircuit->getTotalPowerOfCircuitSources()), myCircuit->getTotalCurrentOfCircuitSources(), myCircuit->getAlphaBest(), myCircuit->getAlphaReason());
     697             : 
     698         360 :     for (auto* it : myElecHybrid) {
     699             : 
     700             :         Element* vehElem = it->getVehElem();
     701         180 :         double voltage = vehElem->getVoltage();
     702         180 :         double current = -vehElem->getCurrent();  // Vehicle is a power source, hence its current (returned by getCurrent()) flows in opposite direction
     703             : 
     704         180 :         it->setCurrentFromOverheadWire(current);
     705         180 :         it->setVoltageOfOverheadWire(voltage);
     706             : 
     707             :         // Calulate energy charged
     708         180 :         double energyIn = WATT2WATTHR(voltage * current);  // [Wh]
     709             : 
     710             :         // Compute energy charged into/from battery considering recuperation and propulsion efficiency (not considering battery capacity)
     711         180 :         double energyCharged = it->computeChargedEnergy(energyIn);
     712             : 
     713             :         // Update energy saved in the battery pack and return trully charged energy considering limits of battery
     714         180 :         double realEnergyCharged = it->storeEnergyToBattery(energyCharged);
     715             : 
     716         180 :         it->setEnergyCharged(realEnergyCharged);
     717             : 
     718             :         // Add energy wasted to the total sum
     719         180 :         it->updateTotalEnergyWasted(energyCharged - realEnergyCharged);
     720             :         // Add the energy provided by the overhead wire segment to the output of the segment
     721         180 :         it->getActOverheadWireSegment()->addChargeValueForOutput(energyIn, it);
     722             :     }
     723             : 
     724         180 :     return 0;
     725             : }
     726             : 
     727             : void
     728         180 : MSTractionSubstation::addChargeValueForOutput(double energy, double current, double alpha, Circuit::alphaFlag alphaReason) {
     729         180 :     std::string status = "";
     730             : 
     731         180 :     myTotalEnergy += energy; //[Wh]
     732             : 
     733         180 :     std::string vehicleIDs = "";
     734         360 :     for (std::vector<MSDevice_ElecHybrid*>::iterator it = myElecHybrid.begin(); it != myElecHybrid.end(); it++) {
     735         360 :         vehicleIDs += (*it)->getID() + " ";
     736             :     }
     737             :     //vehicleIDs.erase(vehicleIDs.end());
     738             :     // TODO vehicleIDs should not be empty, but in some case, it is (due to teleporting of vehicle?)
     739         180 :     if (!vehicleIDs.empty()) {
     740             :         vehicleIDs.pop_back();
     741             :     }
     742             : 
     743         180 :     std::string currents = "";
     744         180 :     currents = myCircuit->getCurrentsOfCircuitSource(currents);
     745             : 
     746             :     // create charge row and insert it in myChargeValues
     747             :     chargeTS C(MSNet::getInstance()->getCurrentTimeStep(), getID(), vehicleIDs, energy, current, currents, mySubstationVoltage, status,
     748         905 :                (int)myElecHybrid.size(), (int)getCircuit()->getNumVoltageSources(), alpha, alphaReason);
     749         180 :     myChargeValues.push_back(C);
     750         360 : }
     751             : 
     752             : void
     753           8 : MSTractionSubstation::writeTractionSubstationOutput(OutputDevice& output) {
     754           8 :     output.openTag(SUMO_TAG_TRACTION_SUBSTATION);
     755           8 :     output.writeAttr(SUMO_ATTR_ID, myID);
     756           8 :     output.writeAttr(SUMO_ATTR_TOTALENERGYCHARGED, myTotalEnergy); //[Wh]
     757           8 :     double length = 0;
     758          50 :     for (auto it = myOverheadWireSegments.begin(); it != myOverheadWireSegments.end(); it++) {
     759          42 :         length += (*it)->getEndLanePosition() - (*it)->getBeginLanePosition();
     760             :     }
     761             :     output.writeAttr(SUMO_ATTR_LENGTH, length);
     762          16 :     output.writeAttr("numVoltageSources", myCircuit->getNumVoltageSources());
     763          16 :     output.writeAttr("numClamps", myOverheadWireClamps.size());
     764          16 :     output.writeAttr(SUMO_ATTR_CHARGINGSTEPS, myChargeValues.size());
     765             : 
     766             :     // start writting
     767           8 :     if (myChargeValues.size() > 0) {
     768             :         // iterate over charging values
     769         184 :         for (std::vector<MSTractionSubstation::chargeTS>::const_iterator i = myChargeValues.begin(); i != myChargeValues.end(); i++) {
     770             :             // open tag for timestep and write all parameters
     771         180 :             output.openTag(SUMO_TAG_STEP);
     772         180 :             output.writeAttr(SUMO_ATTR_TIME, time2string(i->timeStep));
     773             :             // charge values
     774         180 :             output.writeAttr("vehicleIDs", i->vehicleIDs);
     775         180 :             output.writeAttr("numVehicles", i->numVehicles);
     776             :             // same number of numVoltageSources for all time, parameter is written in the superordinate tag
     777             :             //output.writeAttr("numVoltageSources", i->numVoltageSources);
     778             :             // charging status is always ""
     779             :             //output.writeAttr(SUMO_ATTR_CHARGING_STATUS, i->status);
     780         180 :             output.writeAttr(SUMO_ATTR_ENERGYCHARGED, i->energy);
     781         180 :             output.writeAttr(SUMO_ATTR_CURRENTFROMOVERHEADWIRE, i->current);
     782         180 :             output.writeAttr("currents", i->currentsString);
     783             :             // charging values of charging station in this timestep
     784         180 :             output.writeAttr(SUMO_ATTR_VOLTAGE, i->voltage);
     785         180 :             output.writeAttr(SUMO_ATTR_ALPHACIRCUITSOLVER, i->alpha);
     786         180 :             output.writeAttr("alphaFlag", i->alphaReason);
     787             :             // close tag timestep
     788         360 :             output.closeTag();
     789             :             // update timestep of charge
     790             :         }
     791             :     }
     792             :     // close charging station tag
     793           8 :     output.closeTag();
     794           8 : }
     795             : 
     796             : /****************************************************************************/

Generated by: LCOV version 1.14