CARLA
Loading...
Searching...
No Matches
Episode.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
8
9#include "carla/Logging.h"
14
15#include <exception>
16
17class FPoseSnapShot;
18
19namespace carla {
20namespace client {
21namespace detail {
22
23using namespace std::chrono_literals;
24
25 static auto &CastData(const sensor::SensorData &data) {
26 using target_t = const sensor::data::RawEpisodeState;
27 return static_cast<target_t &>(data);
28 }
29
30 template <typename RangeT>
31 static auto GetActorsById_Impl(Client &client, CachedActorList &actors, const RangeT &actor_ids) {
32 auto missing_ids = actors.GetMissingIds(actor_ids);
33 if (!missing_ids.empty()) {
34 actors.InsertRange(client.GetActorsById(missing_ids));
35 }
36 return actors.GetActorsById(actor_ids);
37 }
38
39 Episode::Episode(Client &client, std::weak_ptr<Simulator> simulator)
40 : Episode(client, client.GetEpisodeInfo(), simulator) {}
41
42 Episode::Episode(Client &client, const rpc::EpisodeInfo &info, std::weak_ptr<Simulator> simulator)
43 : _client(client),
44 _state(std::make_shared<EpisodeState>(info.id)),
45 _token(info.token),
46 _simulator(simulator),
47 _handled_episode_id(info.id) {}
48
50 if (_tm_reset_thread.joinable()) {
51 _tm_reset_thread.join();
52 }
53 try {
55 } catch (const std::exception &e) {
56 log_error("exception trying to disconnect from episode:", e.what());
57 }
58 }
59
61 std::weak_ptr<Episode> weak = shared_from_this();
62 _client.SubscribeToStream(_token, [weak](auto buffer) {
63 auto self = weak.lock();
64 if (self != nullptr) {
65
66 auto data = sensor::Deserializer::Deserialize(std::move(buffer));
67 auto next = std::make_shared<const EpisodeState>(CastData(*data));
68 auto prev = self->GetState();
69
70 // TODO: Update how the map change is detected
71 bool HasMapChanged = next->HasMapChanged();
72 bool UpdateLights = next->IsLightUpdatePending();
73
74 /// Check for pending exceptions (Mainly TM server closed)
75 if(self->_pending_exceptions) {
76
77 /// Mark pending exception false
78 self->_pending_exceptions = false;
79
80 /// Create exception for the error message
81 auto exception(self->_pending_exceptions_msg);
82 // Notify waiting threads that exception occurred
83 self->_snapshot.SetException(std::runtime_error(exception));
84 }
85 /// Sensor case: inconsistent data
86 else {
87 bool episode_changed = (next->GetEpisodeId() != prev->GetEpisodeId());
88
89 if (episode_changed) {
90 // Drop the previous episode's WalkerNavigation BEFORE the new
91 // episode id becomes visible (the compare_exchange below is what
92 // unblocks load_world): it caches that map's navmesh (.bin) and
93 // its server-side-navigation probe, so any query through it --
94 // e.g. get_random_location_from_navigation -- would keep
95 // answering with the previous map's data after a load_world.
96 // OnEpisodeStarted() used to do this but is no longer called on
97 // episode change (see OnEpisodeChanged below).
98 self->_walker_navigation.reset();
99 // Same ordering rule for the map cache: load_world returns as
100 // soon as the new episode id is published below, and the first
101 // world.get_map() must already see the cache as stale.
102 self->_should_update_map = true;
103 }
104
105 do {
106 if (prev->GetFrame() >= next->GetFrame() && !episode_changed) {
107 self->_on_tick_callbacks.Call(next);
108 return;
109 }
110 } while (!self->_state.compare_exchange(&prev, next));
111
112 if(UpdateLights || HasMapChanged) {
113 // The LightManager queries the server here; a time-out or a dying Simulator must not terminate the process.
114 try {
115 self->_on_light_update_callbacks.Call(next);
116 } catch (const std::exception &e) {
117 log_error("failed to update the lights for the new snapshot:", e.what());
118 }
119 }
120
121 if(HasMapChanged) {
122 self->_should_update_map = true;
123 }
124
125 /// Episode change
126 if(episode_changed) {
127 self->OnEpisodeChanged(next->GetEpisodeId());
128 }
129
130 // Notify waiting threads and do the callbacks.
131 self->_snapshot.SetValue(next);
132
133 // Call user callbacks.
134 self->_on_tick_callbacks.Call(next);
135 }
136 }
137 });
138 }
139
140 std::optional<rpc::Actor> Episode::GetActorById(ActorId id) {
141 auto actor = _actors.GetActorById(id);
142 if (!actor.has_value()) {
143 auto actor_list = _client.GetActorsById({id});
144 if (!actor_list.empty()) {
145 actor = std::move(actor_list.front());
146 _actors.Insert(*actor);
147 }
148 }
149 return actor;
150 }
151
152 std::vector<rpc::Actor> Episode::GetActorsById(const std::vector<ActorId> &actor_ids) {
153 return GetActorsById_Impl(_client, _actors, actor_ids);
154 }
155
156 std::vector<rpc::Actor> Episode::GetActors() {
157 return GetActorsById_Impl(_client, _actors, GetState()->GetActorIds());
158 }
159
166
167 void Episode::OnEpisodeChanged(uint64_t episode_id) {
168 // Off the stream thread: the reset takes seconds and would hold back every snapshot.
169 if (_tm_reset_thread.joinable()) {
170 _tm_reset_thread.join();
171 }
172 _tm_reset_thread = std::thread([this, episode_id]() {
173 try {
175 } catch (const std::exception &e) {
176 log_error("failed to reset the Traffic Manager for the new episode:", e.what());
177 }
178 _handled_episode_id.store(episode_id);
179 });
180 }
181
184 _should_update_map = false;
185 return true;
186 }
187 return false;
188 }
189
190 std::shared_ptr<WalkerNavigation> Episode::CreateNavigationIfMissing() {
191 std::shared_ptr<WalkerNavigation> nav;
192 do {
193 nav = _walker_navigation.load();
194 if (nav == nullptr) {
195 // Queried once per episode: _walker_navigation is reset on episode
196 // change (see Listen), so a load_world onto/off a server-side-nav
197 // map re-detects.
198 auto new_nav = std::make_shared<WalkerNavigation>(
200 _walker_navigation.compare_exchange(&nav, new_nav);
201 }
202 } while (nav == nullptr);
203 return nav;
204 }
205
206} // namespace detail
207} // namespace client
208} // namespace carla
Keeps a list of actor descriptions to avoid requesting each time the descriptions to the server.
void InsertRange(RangeT range)
Inserts a range containing actors.
void Insert(rpc::Actor actor)
Inserts an actor into the list.
std::optional< rpc::Actor > GetActorById(ActorId id) const
Retrieve the actor matching id, or empty optional if actor is not cached.
std::vector< rpc::Actor > GetActorsById(const RangeT &range) const
Retrieve the actors matching the ids in range.
std::vector< ActorId > GetMissingIds(const RangeT &range) const
Return the actor ids present in range that haven't been added to this list.
Provides communication with the rpc and streaming servers of a CARLA simulator.
std::vector< rpc::Actor > GetActorsById(const std::vector< ActorId > &ids)
bool IsNavigationServerSide() const
True when the server hosts the pedestrian navigation (a World-Partitioned navmesh exists in the curre...
void UnSubscribeFromStream(const streaming::Token &token)
void SubscribeToStream(const streaming::Token &token, std::function< void(Buffer)> callback)
Represents the state of all the actors of an episode at a given frame.
Holds the current episode, and the current episode state.
Definition Episode.h:38
std::weak_ptr< Simulator > _simulator
Definition Episode.h:143
CallbackList< WorldSnapshot > _on_tick_callbacks
Definition Episode.h:127
std::shared_ptr< WalkerNavigation > CreateNavigationIfMissing()
Definition Episode.cpp:190
std::atomic< uint64_t > _handled_episode_id
Definition Episode.h:145
std::vector< rpc::Actor > GetActorsById(const std::vector< ActorId > &actor_ids)
Definition Episode.cpp:152
std::shared_ptr< const EpisodeState > GetState() const
Definition Episode.h:56
std::optional< rpc::Actor > GetActorById(ActorId id)
Definition Episode.cpp:140
void OnEpisodeChanged(uint64_t episode_id)
Definition Episode.cpp:167
const streaming::Token _token
Definition Episode.h:137
AtomicSharedPtr< WalkerNavigation > _walker_navigation
Definition Episode.h:135
std::vector< rpc::Actor > GetActors()
Definition Episode.cpp:156
Episode(Client &client, std::weak_ptr< Simulator > simulator)
Definition Episode.cpp:39
static SharedPtr< SensorData > Deserialize(Buffer &&buffer)
Base class for all the objects containing data generated by a sensor.
Definition SensorData.h:22
State of the episode at a given frame.
static void Reset(uint64_t episode_id)
static auto GetActorsById_Impl(Client &client, CachedActorList &actors, const RangeT &actor_ids)
Definition Episode.cpp:31
static auto & CastData(const sensor::SensorData &data)
Definition Episode.cpp:25
This file contains definitions of common data structures used in traffic manager.
static void log_error(Args &&... args)
Definition Logging.h:110