Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2013-2026 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 MSDevice_FCD.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Michael Behrisch
17 : /// @author Jakob Erdmann
18 : /// @date 11.06.2013
19 : ///
20 : // A device which stands as an implementation FCD and which outputs movereminder calls
21 : /****************************************************************************/
22 : #include <config.h>
23 :
24 : #include <bitset>
25 : #include <utils/common/StringUtils.h>
26 : #include <utils/options/OptionsCont.h>
27 : #include <utils/iodevices/OutputDevice.h>
28 : #include <utils/vehicle/SUMOVehicle.h>
29 : #include <utils/shapes/ShapeContainer.h>
30 : #include <utils/shapes/SUMOPolygon.h>
31 : #include <microsim/MSNet.h>
32 : #include <microsim/MSLane.h>
33 : #include <microsim/MSEdge.h>
34 : #include <microsim/MSVehicle.h>
35 : #include "MSDevice_FCD.h"
36 :
37 :
38 : // ===========================================================================
39 : // static members
40 : // ===========================================================================
41 : SUMOTime MSDevice_FCD::myBegin = SUMOTime_MAX;
42 : SUMOTime MSDevice_FCD::myPeriod = 0;
43 : bool MSDevice_FCD::myUseGeo;
44 : bool MSDevice_FCD::myUseUTM;
45 : double MSDevice_FCD::myMaxLeaderDistance;
46 : std::vector<std::string> MSDevice_FCD::myParamsToWrite;
47 : double MSDevice_FCD::myRadius;
48 : std::set<const MSEdge*> MSDevice_FCD::myEdgeFilter;
49 : std::vector<PositionVector> MSDevice_FCD::myShape4Filters;
50 : bool MSDevice_FCD::myEdgeFilterInitialized(false);
51 : bool MSDevice_FCD::myShapeFilterInitialized(false);
52 : bool MSDevice_FCD::myShapeFilterDesired(false);
53 : SumoXMLAttrMask MSDevice_FCD::myWrittenAttributes;
54 : bool MSDevice_FCD::mySkipEmpty(false);
55 :
56 :
57 : // ===========================================================================
58 : // method definitions
59 : // ===========================================================================
60 : // ---------------------------------------------------------------------------
61 : // static initialisation methods
62 : // ---------------------------------------------------------------------------
63 : void
64 45329 : MSDevice_FCD::insertOptions(OptionsCont& oc) {
65 45329 : oc.addOptionSubTopic("FCD Device");
66 90658 : insertDefaultAssignmentOptions("fcd", "FCD Device", oc);
67 :
68 90658 : oc.doRegister("device.fcd.begin", new Option_String("-1"));
69 90658 : oc.addDescription("device.fcd.begin", "FCD Device", TL("Recording begin time for FCD-data"));
70 :
71 90658 : oc.doRegister("device.fcd.period", new Option_String("0"));
72 90658 : oc.addDescription("device.fcd.period", "FCD Device", TL("Recording period for FCD-data"));
73 :
74 45329 : oc.doRegister("device.fcd.radius", new Option_Float(0));
75 90658 : oc.addDescription("device.fcd.radius", "FCD Device", TL("Record objects in a radius around equipped vehicles"));
76 45329 : }
77 :
78 :
79 : void
80 5256330 : MSDevice_FCD::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
81 5256330 : OptionsCont& oc = OptionsCont::getOptions();
82 10512660 : if (equippedByDefaultAssignmentOptions(oc, "fcd", v, oc.isSet("fcd-output"))) {
83 22514 : MSDevice_FCD* device = new MSDevice_FCD(v, "fcd_" + v.getID());
84 22514 : into.push_back(device);
85 : }
86 5256330 : }
87 :
88 :
89 : // ---------------------------------------------------------------------------
90 : // MSDevice_FCD-methods
91 : // ---------------------------------------------------------------------------
92 22514 : MSDevice_FCD::MSDevice_FCD(SUMOVehicle& holder, const std::string& id) :
93 22514 : MSVehicleDevice(holder, id) {
94 22514 : }
95 :
96 :
97 45022 : MSDevice_FCD::~MSDevice_FCD() {
98 45022 : }
99 :
100 :
101 : SumoXMLAttrMask
102 6811 : MSDevice_FCD::getDefaultMask() {
103 : SumoXMLAttrMask mask;
104 : mask.set(SUMO_ATTR_X);
105 : mask.set(SUMO_ATTR_Y);
106 6811 : if (MSNet::getInstance()->hasElevation()) {
107 : mask.set(SUMO_ATTR_Z);
108 : }
109 : mask.set(SUMO_ATTR_ANGLE);
110 : mask.set(SUMO_ATTR_TYPE);
111 : mask.set(SUMO_ATTR_SPEED);
112 : mask.set(SUMO_ATTR_POSITION);
113 : mask.set(SUMO_ATTR_LANE); // for micro vehicles only
114 : mask.set(SUMO_ATTR_EDGE); // for persons and meso vehicles
115 : mask.set(SUMO_ATTR_SLOPE);
116 18323 : if (!MSGlobals::gUseMesoSim && OptionsCont::getOptions().getFloat("fcd-output.max-leader-distance") > 0.) {
117 : mask.set(SUMO_ATTR_LEADER_ID);
118 : mask.set(SUMO_ATTR_LEADER_SPEED);
119 : mask.set(SUMO_ATTR_LEADER_GAP);
120 : }
121 6811 : return mask;
122 : }
123 :
124 :
125 : bool
126 5392 : MSDevice_FCD::shapeFilter(const SUMOTrafficObject* veh) {
127 : // lazily build the shape filter in the case where route file is loaded as an additional file
128 5392 : if (!myShapeFilterInitialized) {
129 0 : buildShapeFilter();
130 : }
131 5392 : const MSVehicle* msVeh = dynamic_cast<const MSVehicle*>(veh);
132 9764 : for (auto shape : myShape4Filters) {
133 5392 : if (shape.around(veh->getPosition()) || ((msVeh != nullptr) && shape.around(msVeh->getBackPosition()))) {
134 : return true;
135 : }
136 5392 : }
137 4372 : return false;
138 : }
139 :
140 :
141 : void
142 12 : MSDevice_FCD::buildShapeFilter(void) {
143 12 : const OptionsCont& oc = OptionsCont::getOptions();
144 24 : if (oc.isSet("fcd-output.filter-shapes")) {
145 12 : const ShapeContainer& loadedShapes = MSNet::getInstance()->getShapeContainer();
146 12 : if (loadedShapes.getPolygons().size() > 0) {
147 36 : for (std::string attrName : oc.getStringVector("fcd-output.filter-shapes")) {
148 12 : if (loadedShapes.getPolygons().get(attrName) == 0) {
149 0 : WRITE_ERRORF(TL("Specified shape '%' for filtering fcd-output could not be found."), attrName);
150 : } else {
151 : // store the PositionVector, not reference, as traci can manipulate / detete the polygons
152 12 : myShape4Filters.push_back(loadedShapes.getPolygons().get(attrName)->getShape());
153 : }
154 : }
155 12 : myShapeFilterInitialized = true;
156 : }
157 : } else {
158 0 : myShapeFilterInitialized = true;
159 : }
160 12 : }
161 :
162 :
163 : void
164 6691631 : MSDevice_FCD::initOnce() {
165 6691631 : if (myEdgeFilterInitialized) {
166 6687327 : return;
167 : }
168 4304 : myEdgeFilterInitialized = true;
169 4304 : const OptionsCont& oc = OptionsCont::getOptions();
170 4304 : myPeriod = string2time(oc.getString("device.fcd.period"));
171 4304 : myBegin = string2time(oc.getString("device.fcd.begin"));
172 4304 : myUseGeo = oc.getBool("fcd-output.geo");
173 4304 : myUseUTM = oc.getBool("fcd-output.utm");
174 4304 : myMaxLeaderDistance = oc.getFloat("fcd-output.max-leader-distance");
175 4304 : myParamsToWrite = oc.getStringVector("fcd-output.params");
176 4304 : myRadius = oc.getFloat("device.fcd.radius");
177 4304 : mySkipEmpty = oc.getBool("fcd-output.skip-empty");
178 8608 : if (oc.isSet("fcd-output.filter-edges.input-file")) {
179 12 : const std::string file = oc.getString("fcd-output.filter-edges.input-file");
180 6 : std::ifstream strm(file.c_str());
181 6 : if (!strm.good()) {
182 0 : throw ProcessError(TLF("Could not load names of edges for filtering fcd-output from '%'.", file));
183 : }
184 12 : while (strm.good()) {
185 : std::string name;
186 6 : strm >> name;
187 : // maybe we're loading an edge-selection
188 12 : if (StringUtils::startsWith(name, "edge:")) {
189 12 : name = name.substr(5);
190 : }
191 6 : myEdgeFilter.insert(MSEdge::dictionary(name));
192 : }
193 6 : }
194 : SumoXMLAttrMask emissions;
195 : emissions.set(SUMO_ATTR_ECLASS);
196 : emissions.set(SUMO_ATTR_CO2);
197 : emissions.set(SUMO_ATTR_CO);
198 : emissions.set(SUMO_ATTR_HC);
199 : emissions.set(SUMO_ATTR_NOX);
200 : emissions.set(SUMO_ATTR_PMX);
201 : emissions.set(SUMO_ATTR_FUEL);
202 : emissions.set(SUMO_ATTR_ELECTRICITY);
203 : emissions.set(SUMO_ATTR_NOISE);
204 : SumoXMLAttrMask misc;
205 : misc.set(SUMO_ATTR_SIGNALS);
206 : misc.set(SUMO_ATTR_ACCELERATION);
207 : misc.set(SUMO_ATTR_ACCELERATION_LAT);
208 : misc.set(SUMO_ATTR_SPEED_VEC);
209 : misc.set(SUMO_ATTR_ACCEL_VEC);
210 : misc.set(SUMO_ATTR_DISTANCE);
211 : misc.set(SUMO_ATTR_ODOMETER);
212 : misc.set(SUMO_ATTR_POSITION_LAT);
213 : misc.set(SUMO_ATTR_SPEED_LAT);
214 : misc.set(SUMO_ATTR_SPEEDREL);
215 : misc.set(SUMO_ATTR_LEADER_ID);
216 : misc.set(SUMO_ATTR_LEADER_SPEED);
217 : misc.set(SUMO_ATTR_ARRIVALDELAY);
218 : misc.set(SUMO_ATTR_DELAY);
219 : misc.set(SUMO_ATTR_SEGMENT);
220 : misc.set(SUMO_ATTR_QUEUE);
221 : misc.set(SUMO_ATTR_ENTRYTIME);
222 : misc.set(SUMO_ATTR_EVENTTIME);
223 : misc.set(SUMO_ATTR_BLOCKTIME);
224 21520 : const std::map<std::string, SumoXMLAttrMask> special = {{"location", getDefaultMask()}, {"emissions", emissions}, {"misc", misc}};
225 8608 : if (oc.isSet("fcd-output.attributes")) {
226 3594 : myWrittenAttributes = OutputDevice::parseWrittenAttributes(oc.getStringVector("fcd-output.attributes"), "fcd output", special);
227 : } else {
228 2507 : myWrittenAttributes = getDefaultMask();
229 : }
230 : // need to store this because some attributes are reset later
231 : const bool all = myWrittenAttributes.all();
232 : myWrittenAttributes.set(SUMO_ATTR_ID);
233 4304 : if (!MSNet::getInstance()->hasElevation()) {
234 : myWrittenAttributes.reset(SUMO_ATTR_Z);
235 : }
236 8608 : if (oc.getBool("fcd-output.signals")) {
237 : myWrittenAttributes.set(SUMO_ATTR_SIGNALS);
238 : }
239 8608 : if (oc.getBool("fcd-output.acceleration")) {
240 : myWrittenAttributes.set(SUMO_ATTR_ACCELERATION);
241 : }
242 4304 : myWrittenAttributes.set(SUMO_ATTR_ACCELERATION_LAT, myWrittenAttributes.test(SUMO_ATTR_ACCELERATION) && MSGlobals::gSublane);
243 8608 : if (oc.getBool("fcd-output.distance")) {
244 : myWrittenAttributes.set(SUMO_ATTR_DISTANCE);
245 : }
246 8608 : if (oc.getBool("fcd-output.speed-relative")) {
247 : myWrittenAttributes.set(SUMO_ATTR_SPEEDREL);
248 : }
249 :
250 8608 : if (oc.isSet("fcd-output.filter-shapes")) {
251 : // build the shape filter if it is desired
252 12 : myShapeFilterDesired = true;
253 12 : buildShapeFilter();
254 : }
255 8608 : OutputDevice::getDeviceByOption("fcd-output").setExpectedAttributes(all ? 0 : myWrittenAttributes);
256 0 : }
257 :
258 :
259 : void
260 42169 : MSDevice_FCD::cleanup() {
261 : myEdgeFilter.clear();
262 : myShape4Filters.clear();
263 42169 : myEdgeFilterInitialized = false;
264 42169 : myShapeFilterInitialized = false;
265 42169 : myShapeFilterDesired = false;
266 : myWrittenAttributes.reset();
267 42169 : }
268 :
269 :
270 : /****************************************************************************/
|