Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/libtraci/Connection.cpp
169665 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2012-2025 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 Connection.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Mario Krumnow
17
/// @author Jakob Erdmann
18
/// @author Michael Behrisch
19
/// @date 30.05.2012
20
///
21
// C++ TraCI client API implementation
22
/****************************************************************************/
23
#include <config.h>
24
25
#include <thread>
26
#include <chrono>
27
#include <array>
28
#include <libsumo/StorageHelper.h>
29
#include <libsumo/TraCIDefs.h>
30
#include "Connection.h"
31
32
33
namespace libtraci {
34
// ===========================================================================
35
// static member initializations
36
// ===========================================================================
37
Connection* Connection::myActive = nullptr;
38
std::map<const std::string, Connection*> Connection::myConnections;
39
40
41
// ===========================================================================
42
// member method definitions
43
// ===========================================================================
44
Connection::Connection(const std::string& host, int port, int numRetries, const std::string& label, FILE* const pipe) :
45
myLabel(label), myProcessPipe(pipe), myProcessReader(nullptr), mySocket(host, port) {
46
if (pipe != nullptr) {
47
myProcessReader = new std::thread(&Connection::readOutput, this);
48
}
49
for (int i = 0; i <= numRetries; i++) {
50
try {
51
mySocket.connect();
52
break;
53
} catch (tcpip::SocketException& e) {
54
mySocket.close();
55
if (i == numRetries) {
56
close();
57
throw libsumo::FatalTraCIError("Could not connect in " + toString(numRetries + 1) + " tries");
58
}
59
std::cout << "Could not connect to TraCI server at " << host << ":" << port << " " << e.what() << std::endl;
60
std::cout << " Retrying in 1 second" << std::endl;
61
std::this_thread::sleep_for(std::chrono::seconds(1));
62
}
63
}
64
}
65
66
67
void
68
Connection::readOutput() {
69
std::array<char, 256> buffer;
70
bool errout = false;
71
while (fgets(buffer.data(), (int)buffer.size(), myProcessPipe) != nullptr) {
72
std::stringstream result;
73
result << buffer.data();
74
std::string line;
75
while (std::getline(result, line)) {
76
if ((errout && (line.empty() || line[0] == ' ')) || line.compare(0, 6, "Error:") == 0 || line.compare(0, 8, "Warning:") == 0) {
77
std::cerr << line << std::endl;
78
errout = true;
79
} else {
80
std::cout << line << std::endl;
81
errout = false;
82
}
83
}
84
}
85
}
86
87
88
void
89
Connection::close() {
90
if (mySocket.has_client_connection()) {
91
std::unique_lock<std::mutex> lock{ myMutex };
92
tcpip::Storage outMsg;
93
// command length
94
outMsg.writeUnsignedByte(1 + 1);
95
// command id
96
outMsg.writeUnsignedByte(libsumo::CMD_CLOSE);
97
mySocket.sendExact(outMsg);
98
99
tcpip::Storage inMsg;
100
std::string acknowledgement;
101
check_resultState(inMsg, libsumo::CMD_CLOSE, false, &acknowledgement);
102
mySocket.close();
103
}
104
if (myProcessReader != nullptr) {
105
myProcessReader->join();
106
delete myProcessReader;
107
myProcessReader = nullptr;
108
#ifdef WIN32
109
_pclose(myProcessPipe);
110
#else
111
pclose(myProcessPipe);
112
#endif
113
}
114
myConnections.erase(myLabel);
115
delete myActive;
116
myActive = nullptr;
117
}
118
119
120
void
121
Connection::simulationStep(double time) {
122
std::unique_lock<std::mutex> lock{myMutex};
123
tcpip::Storage outMsg;
124
// command length
125
outMsg.writeUnsignedByte(1 + 1 + 8);
126
// command id
127
outMsg.writeUnsignedByte(libsumo::CMD_SIMSTEP);
128
outMsg.writeDouble(time);
129
// send request message
130
mySocket.sendExact(outMsg);
131
132
tcpip::Storage inMsg;
133
check_resultState(inMsg, libsumo::CMD_SIMSTEP);
134
mySubscriptionResults.clear();
135
myContextSubscriptionResults.clear();
136
int numSubs = inMsg.readInt();
137
while (numSubs-- > 0) {
138
const int responseID = check_commandGetResult(inMsg, 0, -1, true);
139
if ((responseID >= libsumo::RESPONSE_SUBSCRIBE_INDUCTIONLOOP_VARIABLE && responseID <= libsumo::RESPONSE_SUBSCRIBE_BUSSTOP_VARIABLE) ||
140
(responseID >= libsumo::RESPONSE_SUBSCRIBE_PARKINGAREA_VARIABLE && responseID <= libsumo::RESPONSE_SUBSCRIBE_OVERHEADWIRE_VARIABLE)) {
141
readVariableSubscription(responseID, inMsg);
142
} else {
143
readContextSubscription(responseID, inMsg);
144
}
145
}
146
}
147
148
149
void
150
Connection::setOrder(int order) {
151
std::unique_lock<std::mutex> lock{ myMutex };
152
tcpip::Storage outMsg;
153
// command length
154
outMsg.writeUnsignedByte(1 + 1 + 4);
155
// command id
156
outMsg.writeUnsignedByte(libsumo::CMD_SETORDER);
157
// client index
158
outMsg.writeInt(order);
159
mySocket.sendExact(outMsg);
160
161
tcpip::Storage inMsg;
162
check_resultState(inMsg, libsumo::CMD_SETORDER);
163
}
164
165
166
void
167
Connection::createCommand(int cmdID, int varID, const std::string* const objID, tcpip::Storage* add) const {
168
if (!mySocket.has_client_connection()) {
169
throw libsumo::FatalTraCIError("Connection already closed.");
170
}
171
myOutput.reset();
172
// command length
173
int length = 1 + 1;
174
if (varID >= 0) {
175
length += 1;
176
if (objID != nullptr) {
177
length += 4 + (int)objID->length();
178
}
179
}
180
if (add != nullptr) {
181
length += (int)add->size();
182
}
183
if (length <= 255) {
184
myOutput.writeUnsignedByte(length);
185
} else {
186
myOutput.writeUnsignedByte(0);
187
myOutput.writeInt(length + 4);
188
}
189
myOutput.writeUnsignedByte(cmdID);
190
if (varID >= 0) {
191
myOutput.writeUnsignedByte(varID);
192
if (objID != nullptr) {
193
myOutput.writeString(*objID);
194
}
195
}
196
// additional values
197
if (add != nullptr) {
198
myOutput.writeStorage(*add);
199
}
200
}
201
202
203
void
204
Connection::subscribe(int domID, const std::string& objID, double beginTime, double endTime,
205
int domain, double range, const std::vector<int>& vars, const libsumo::TraCIResults& params) {
206
if (!mySocket.has_client_connection()) {
207
throw tcpip::SocketException("Socket is not initialised");
208
}
209
const bool isContext = domain != -1;
210
tcpip::Storage outMsg;
211
outMsg.writeUnsignedByte(domID); // command id
212
outMsg.writeDouble(beginTime);
213
outMsg.writeDouble(endTime);
214
outMsg.writeString(objID);
215
if (isContext) {
216
outMsg.writeUnsignedByte(domain);
217
outMsg.writeDouble(range);
218
}
219
if (vars.size() == 1 && vars.front() == -1) {
220
if (domID == libsumo::CMD_SUBSCRIBE_VEHICLE_VARIABLE && !isContext) {
221
// default for vehicles is edge id and lane position
222
outMsg.writeUnsignedByte(2);
223
outMsg.writeUnsignedByte(libsumo::VAR_ROAD_ID);
224
outMsg.writeUnsignedByte(libsumo::VAR_LANEPOSITION);
225
} else {
226
// default for detectors is vehicle number, for all others (and contexts) id list
227
outMsg.writeUnsignedByte(1);
228
const bool isDetector = domID == libsumo::CMD_SUBSCRIBE_INDUCTIONLOOP_VARIABLE
229
|| domID == libsumo::CMD_SUBSCRIBE_LANEAREA_VARIABLE
230
|| domID == libsumo::CMD_SUBSCRIBE_MULTIENTRYEXIT_VARIABLE
231
|| domID == libsumo::CMD_SUBSCRIBE_LANE_VARIABLE
232
|| domID == libsumo::CMD_SUBSCRIBE_EDGE_VARIABLE;
233
outMsg.writeUnsignedByte(isDetector ? libsumo::LAST_STEP_VEHICLE_NUMBER : libsumo::TRACI_ID_LIST);
234
}
235
} else {
236
outMsg.writeUnsignedByte((int)vars.size());
237
for (const int v : vars) {
238
outMsg.writeUnsignedByte(v);
239
const auto& paramEntry = params.find(v);
240
if (paramEntry != params.end()) {
241
outMsg.writeStorage(*libsumo::StorageHelper::toStorage(*paramEntry->second));
242
}
243
}
244
}
245
tcpip::Storage complete;
246
complete.writeUnsignedByte(0);
247
complete.writeInt(5 + (int)outMsg.size());
248
complete.writeStorage(outMsg);
249
std::unique_lock<std::mutex> lock{ myMutex };
250
// send message
251
mySocket.sendExact(complete);
252
253
tcpip::Storage inMsg;
254
check_resultState(inMsg, domID);
255
if (!vars.empty()) {
256
const int responseID = check_commandGetResult(inMsg, domID);
257
if (isContext) {
258
readContextSubscription(responseID, inMsg);
259
} else {
260
readVariableSubscription(responseID, inMsg);
261
}
262
}
263
}
264
265
266
void
267
Connection::check_resultState(tcpip::Storage& inMsg, int command, bool ignoreCommandId, std::string* acknowledgement) {
268
mySocket.receiveExact(inMsg);
269
int cmdLength;
270
int cmdId;
271
int resultType;
272
int cmdStart;
273
std::string msg;
274
try {
275
cmdStart = inMsg.position();
276
cmdLength = inMsg.readUnsignedByte();
277
cmdId = inMsg.readUnsignedByte();
278
resultType = inMsg.readUnsignedByte();
279
msg = inMsg.readString();
280
} catch (std::invalid_argument&) {
281
throw libsumo::TraCIException("#Error: an exception was thrown while reading result state message");
282
}
283
switch (resultType) {
284
case libsumo::RTYPE_ERR:
285
throw libsumo::TraCIException(msg);
286
case libsumo::RTYPE_NOTIMPLEMENTED:
287
throw libsumo::TraCIException(".. Sent command is not implemented (" + toHex(command) + "), [description: " + msg + "]");
288
case libsumo::RTYPE_OK:
289
if (acknowledgement != nullptr) {
290
(*acknowledgement) = ".. Command acknowledged (" + toHex(command) + "), [description: " + msg + "]";
291
}
292
break;
293
default:
294
throw libsumo::TraCIException(".. Answered with unknown result code(" + toHex(resultType) + ") to command(" + toHex(command) + "), [description: " + msg + "]");
295
}
296
if (command != cmdId && !ignoreCommandId) {
297
throw libsumo::TraCIException("#Error: received status response to command: " + toHex(cmdId) + " but expected: " + toHex(command));
298
}
299
if ((cmdStart + cmdLength) != (int) inMsg.position()) {
300
throw libsumo::TraCIException("#Error: command at position " + toHex(cmdStart) + " has wrong length");
301
}
302
}
303
304
305
int
306
Connection::check_commandGetResult(tcpip::Storage& inMsg, int command, int expectedType, bool ignoreCommandId) const {
307
int length = inMsg.readUnsignedByte();
308
if (length == 0) {
309
length = inMsg.readInt();
310
}
311
int cmdId = inMsg.readUnsignedByte();
312
if (!ignoreCommandId && cmdId != (command + 0x10)) {
313
throw libsumo::TraCIException("#Error: received response with command id: " + toString(cmdId) + "but expected: " + toString(command + 0x10));
314
}
315
if (expectedType >= 0) {
316
// not called from the TraCITestClient but from within the Connection
317
inMsg.readUnsignedByte(); // variableID
318
inMsg.readString(); // objectID
319
int valueDataType = inMsg.readUnsignedByte();
320
if (valueDataType != expectedType) {
321
throw libsumo::TraCIException("Expected " + toString(expectedType) + " but got " + toString(valueDataType));
322
}
323
}
324
return cmdId;
325
}
326
327
328
tcpip::Storage&
329
Connection::doCommand(int command, int var, const std::string& id, tcpip::Storage* add, int expectedType) {
330
createCommand(command, var, &id, add);
331
mySocket.sendExact(myOutput);
332
myInput.reset();
333
check_resultState(myInput, command);
334
if (expectedType >= 0) {
335
check_commandGetResult(myInput, command, expectedType);
336
}
337
return myInput;
338
}
339
340
341
void
342
Connection::addFilter(int var, tcpip::Storage* add) {
343
std::unique_lock<std::mutex> lock{ myMutex };
344
createCommand(libsumo::CMD_ADD_SUBSCRIPTION_FILTER, var, nullptr, add);
345
mySocket.sendExact(myOutput);
346
myInput.reset();
347
check_resultState(myInput, libsumo::CMD_ADD_SUBSCRIPTION_FILTER);
348
}
349
350
351
void
352
Connection::readVariables(tcpip::Storage& inMsg, const std::string& objectID, int variableCount, libsumo::SubscriptionResults& into) {
353
while (variableCount > 0) {
354
355
const int variableID = inMsg.readUnsignedByte();
356
const int status = inMsg.readUnsignedByte();
357
const int type = inMsg.readUnsignedByte();
358
359
if (status == libsumo::RTYPE_OK) {
360
switch (type) {
361
case libsumo::TYPE_DOUBLE:
362
into[objectID][variableID] = std::make_shared<libsumo::TraCIDouble>(inMsg.readDouble());
363
break;
364
case libsumo::TYPE_STRING:
365
into[objectID][variableID] = std::make_shared<libsumo::TraCIString>(inMsg.readString());
366
break;
367
case libsumo::POSITION_2D: {
368
auto p = std::make_shared<libsumo::TraCIPosition>();
369
p->x = inMsg.readDouble();
370
p->y = inMsg.readDouble();
371
into[objectID][variableID] = p;
372
break;
373
}
374
case libsumo::POSITION_3D: {
375
auto p = std::make_shared<libsumo::TraCIPosition>();
376
p->x = inMsg.readDouble();
377
p->y = inMsg.readDouble();
378
p->z = inMsg.readDouble();
379
into[objectID][variableID] = p;
380
break;
381
}
382
case libsumo::TYPE_COLOR: {
383
auto c = std::make_shared<libsumo::TraCIColor>();
384
c->r = (unsigned char)inMsg.readUnsignedByte();
385
c->g = (unsigned char)inMsg.readUnsignedByte();
386
c->b = (unsigned char)inMsg.readUnsignedByte();
387
c->a = (unsigned char)inMsg.readUnsignedByte();
388
into[objectID][variableID] = c;
389
break;
390
}
391
case libsumo::TYPE_INTEGER:
392
into[objectID][variableID] = std::make_shared<libsumo::TraCIInt>(inMsg.readInt());
393
break;
394
case libsumo::TYPE_STRINGLIST: {
395
auto sl = std::make_shared<libsumo::TraCIStringList>();
396
int n = inMsg.readInt();
397
for (int i = 0; i < n; ++i) {
398
sl->value.push_back(inMsg.readString());
399
}
400
into[objectID][variableID] = sl;
401
break;
402
}
403
case libsumo::TYPE_POLYGON: {
404
auto po = std::make_shared<libsumo::TraCIPositionVector>();
405
StoHelp::readPolygon(inMsg, *po);
406
into[objectID][variableID] = po;
407
break;
408
}
409
case libsumo::TYPE_DOUBLELIST: {
410
auto po = std::make_shared<libsumo::TraCIDoubleList>();
411
po->value = inMsg.readDoubleList();
412
into[objectID][variableID] = po;
413
break;
414
}
415
case libsumo::TYPE_COMPOUND: {
416
const int n = inMsg.readInt();
417
if (variableID == libsumo::LAST_STEP_VEHICLE_DATA) {
418
auto r = std::make_shared<libsumo::TraCIVehicleDataVectorWrapped>();
419
StoHelp::readVehicleDataVector(inMsg, r->value);
420
into[objectID][variableID] = r;
421
break;
422
} else if (variableID == libsumo::VAR_NEXT_LINKS) {
423
const int count = StoHelp::readTypedInt(inMsg);
424
auto r = std::make_shared<libsumo::TraCIConnectionVectorWrapped>();
425
for (int i = 0; i < count; ++i) {
426
libsumo::TraCIConnection con;
427
StoHelp::readConnection(inMsg, con);
428
r->value.emplace_back(con);
429
}
430
into[objectID][variableID] = r;
431
break;
432
} else if (variableID == libsumo::VAR_STAGE) {
433
auto r = std::make_shared<libsumo::TraCIStage>();
434
StoHelp::readStage(inMsg, *r);
435
into[objectID][variableID] = r;
436
break;
437
} else if (variableID == libsumo::VAR_TAXI_RESERVATIONS) {
438
auto r = std::make_shared<libsumo::TraCIReservationVectorWrapped>();
439
for (int i = 0; i < n; ++i) {
440
libsumo::TraCIReservation res;
441
StoHelp::readReservation(inMsg, res);
442
r->value.emplace_back(res);
443
}
444
into[objectID][variableID] = r;
445
break;
446
} else if (variableID == libsumo::TL_COMPLETE_DEFINITION_RYG) {
447
auto r = std::make_shared<libsumo::TraCILogicVectorWrapped>();
448
for (int i = 0; i < n; ++i) {
449
libsumo::TraCILogic logic;
450
StoHelp::readLogic(inMsg, logic);
451
r->value.emplace_back(logic);
452
}
453
into[objectID][variableID] = r;
454
break;
455
} else if (variableID == libsumo::TL_CONSTRAINT || variableID == libsumo::TL_CONSTRAINT_BYFOE) {
456
auto r = std::make_shared<libsumo::TraCISignalConstraintVectorWrapped>();
457
StoHelp::readConstraintVector(inMsg, r->value);
458
into[objectID][variableID] = r;
459
break;
460
} else if (variableID == libsumo::TL_CONTROLLED_LINKS) {
461
auto r = std::make_shared<libsumo::TraCILinkVectorVectorWrapped>();
462
StoHelp::readLinkVectorVector(inMsg, r->value);
463
into[objectID][variableID] = r;
464
break;
465
} else if (variableID == libsumo::VAR_BEST_LANES) {
466
auto r = std::make_shared<libsumo::TraCIBestLanesDataVectorWrapped>();
467
StoHelp::readBestLanesVector(inMsg, r->value);
468
into[objectID][variableID] = r;
469
break;
470
} else if (variableID == libsumo::VAR_COLLISIONS) {
471
auto r = std::make_shared<libsumo::TraCICollisionVectorWrapped>();
472
StoHelp::readCollisionVector(inMsg, r->value);
473
into[objectID][variableID] = r;
474
break;
475
} else if (variableID == libsumo::VAR_FOES) {
476
auto r = std::make_shared<libsumo::TraCIJunctionFoeVectorWrapped>();
477
StoHelp::readJunctionFoeVector(inMsg, r->value);
478
into[objectID][variableID] = r;
479
break;
480
} else if (variableID == libsumo::CMD_CHANGELANE) {
481
auto r = std::make_shared<libsumo::TraCIIntList>();
482
r->value.push_back(StoHelp::readTypedInt(inMsg));
483
r->value.push_back(StoHelp::readTypedInt(inMsg));
484
into[objectID][variableID] = r;
485
break;
486
} else if (variableID == libsumo::VAR_NEIGHBORS) {
487
auto r = std::make_shared<libsumo::TraCIStringDoublePairList>();
488
for (int i = 0; i < n; i++) {
489
const std::string neighID = inMsg.readString();
490
r->value.emplace_back(neighID, inMsg.readDouble());
491
}
492
into[objectID][variableID] = r;
493
break;
494
} else if (variableID == libsumo::VAR_NEXT_STOPS2) {
495
auto r = std::make_shared<libsumo::TraCINextStopDataVectorWrapped>();
496
StoHelp::readStopVector(inMsg, r->value);
497
into[objectID][variableID] = r;
498
break;
499
} else if (variableID == libsumo::VAR_NEXT_TLS) {
500
auto r = std::make_shared<libsumo::TraCINextTLSDataVectorWrapped>();
501
StoHelp::readTLSDataVector(inMsg, r->value);
502
into[objectID][variableID] = r;
503
break;
504
}
505
if (n == 2) {
506
const int firstType = inMsg.readUnsignedByte();
507
if (firstType == libsumo::TYPE_STRING) {
508
const std::string s = inMsg.readString();
509
const int secondType = inMsg.readUnsignedByte();
510
if (secondType == libsumo::TYPE_DOUBLE) {
511
auto r = std::make_shared<libsumo::TraCIRoadPosition>();
512
r->edgeID = s;
513
r->pos = inMsg.readDouble();
514
into[objectID][variableID] = r;
515
break;
516
} else if (secondType == libsumo::TYPE_STRING) {
517
auto sl = std::make_shared<libsumo::TraCIStringList>();
518
sl->value.push_back(s);
519
sl->value.push_back(inMsg.readString());
520
into[objectID][variableID] = sl;
521
break;
522
}
523
}
524
}
525
}
526
FALLTHROUGH;
527
// TODO Other data types
528
529
default:
530
throw libsumo::TraCIException("Unimplemented subscription: variableID=" + toHex(variableID) + " type=" + toHex(type));
531
}
532
} else {
533
throw libsumo::TraCIException("Subscription response error: variableID=" + toHex(variableID) + " status=" + toHex(status));
534
}
535
536
variableCount--;
537
}
538
}
539
540
541
void
542
Connection::readVariableSubscription(int responseID, tcpip::Storage& inMsg) {
543
const std::string objectID = inMsg.readString();
544
const int variableCount = inMsg.readUnsignedByte();
545
readVariables(inMsg, objectID, variableCount, mySubscriptionResults[responseID]);
546
}
547
548
549
void
550
Connection::readContextSubscription(int responseID, tcpip::Storage& inMsg) {
551
const std::string contextID = inMsg.readString();
552
inMsg.readUnsignedByte(); // context domain
553
const int variableCount = inMsg.readUnsignedByte();
554
int numObjects = inMsg.readInt();
555
// the following also instantiates the empty map to get comparable results with libsumo
556
// see also https://github.com/eclipse/sumo/issues/7288
557
libsumo::SubscriptionResults& results = myContextSubscriptionResults[responseID][contextID];
558
while (numObjects-- > 0) {
559
const std::string& objectID = inMsg.readString();
560
results[objectID]; // instantiate empty map for id lists
561
readVariables(inMsg, objectID, variableCount, results);
562
}
563
}
564
565
566
}
567
568
569
/****************************************************************************/
570
571