Skip to content

Commit 4b92430

Browse files
committed
Remove first_seen logic from Tracker
1 parent 7ec62f0 commit 4b92430

7 files changed

Lines changed: 4 additions & 161 deletions

File tree

tracker/inc/tracking_types.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ struct Track {
9696
std::array<double, 3> size; ///< Object size [length, width, height] meters
9797
std::array<double, 4> rotation; ///< Orientation quaternion [x, y, z, w]
9898
std::optional<double> confidence; ///< Detection confidence score in [0, 1] (absent if not available)
99-
std::string first_seen_iso; ///< ISO 8601 UTC timestamp when this track was first observed
10099
std::string
101100
metadata_json; ///< Raw JSON string of the detection's metadata object (empty if absent)
102101
};

tracker/inc/tracking_worker.hpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,7 @@ class TrackingWorker {
155155
* @return Vector of Track structs ready for publishing
156156
*/
157157
std::vector<Track> convert_tracks(const std::vector<rv::tracking::TrackedObject>& rv_tracks,
158-
const std::string& category,
159-
std::chrono::system_clock::time_point timestamp);
158+
const std::string& category);
160159

161160
TrackingScope scope_;
162161
std::string scene_name_;
@@ -172,9 +171,6 @@ class TrackingWorker {
172171
// RobotVision int ID -> UUID v4 string mapping (single-thread access, no mutex)
173172
std::unordered_map<int32_t, std::string> id_map_;
174173

175-
// UUID -> ISO 8601 first-seen timestamp (set when track UUID is first assigned)
176-
std::unordered_map<std::string, std::string> first_seen_map_;
177-
178174
std::thread worker_thread_;
179175
mutable std::mutex queue_mutex_;
180176
std::condition_variable queue_cv_;

tracker/schema/scene-data.schema.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,6 @@
112112
"description": "Detection confidence score in [0, 1], passed through from the originating detection",
113113
"minimum": 0,
114114
"maximum": 1
115-
},
116-
"first_seen": {
117-
"type": "string",
118-
"format": "date-time",
119-
"description": "ISO 8601 UTC timestamp when this track was first observed by the tracker"
120115
}
121116
},
122117
"additionalProperties": false

tracker/src/track_publisher.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,6 @@ std::string TrackPublisher::serialize(const std::string& scene_id, const std::st
108108
obj.AddMember("confidence", Value(*track.confidence), allocator);
109109
}
110110

111-
// first_seen (always set by the tracker)
112-
if (!track.first_seen_iso.empty()) {
113-
obj.AddMember(
114-
"first_seen",
115-
Value().SetString(track.first_seen_iso.c_str(),
116-
static_cast<SizeType>(track.first_seen_iso.size()), allocator),
117-
allocator);
118-
}
119-
120111
objects_array.PushBack(obj, allocator);
121112
}
122113

tracker/src/tracking_worker.cpp

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,7 @@ TrackingWorker::transform_detections(const Chunk& chunk) {
215215

216216
std::vector<Track>
217217
TrackingWorker::convert_tracks(const std::vector<rv::tracking::TrackedObject>& rv_tracks,
218-
const std::string& category,
219-
std::chrono::system_clock::time_point timestamp) {
218+
const std::string& category) {
220219
// Extract active RobotVision IDs for map update
221220
std::vector<int32_t> active_ids;
222221
active_ids.reserve(rv_tracks.size());
@@ -225,25 +224,7 @@ TrackingWorker::convert_tracks(const std::vector<rv::tracking::TrackedObject>& r
225224
}
226225

227226
// Update ID map: preserve UUIDs for continuing tracks, generate new for new tracks
228-
auto new_map = update_id_map(id_map_, active_ids);
229-
230-
// Record first_seen for any brand-new UUIDs (not present in the old id_map_)
231-
const std::string ts_iso = formatTimestamp(timestamp);
232-
for (const auto& [rv_id, uuid] : new_map) {
233-
if (!id_map_.contains(rv_id)) {
234-
first_seen_map_.emplace(uuid, ts_iso);
235-
}
236-
}
237-
238-
// Prune first_seen_map_ entries for tracks that have disappeared
239-
for (auto it = first_seen_map_.begin(); it != first_seen_map_.end();) {
240-
// Check if this UUID is still active
241-
bool active = std::any_of(new_map.begin(), new_map.end(),
242-
[&uuid = it->first](const auto& kv) { return kv.second == uuid; });
243-
it = active ? std::next(it) : first_seen_map_.erase(it);
244-
}
245-
246-
id_map_ = std::move(new_map);
227+
id_map_ = update_id_map(id_map_, active_ids);
247228

248229
std::vector<Track> tracks;
249230
tracks.reserve(rv_tracks.size());
@@ -257,11 +238,6 @@ TrackingWorker::convert_tracks(const std::vector<rv::tracking::TrackedObject>& r
257238
track.size = {rv_track.length, rv_track.width, rv_track.height};
258239
track.rotation = CoordinateTransformer::yawToQuaternion(rv_track.yaw);
259240

260-
// Set first_seen from map (always present for active tracks)
261-
if (auto it = first_seen_map_.find(track.id); it != first_seen_map_.end()) {
262-
track.first_seen_iso = it->second;
263-
}
264-
265241
// Retrieve metadata_json stored in attributes by transform_detections().
266242
// NOTE: multi-camera last-write-wins — when the same track is observed by
267243
// multiple cameras in one time chunk, rv::tracking::TrackedObject::attributes
@@ -303,7 +279,7 @@ std::vector<Track> TrackingWorker::match_and_convert(
303279

304280
// Get reliable tracks and map RobotVision int IDs to UUID strings
305281
auto rv_tracks = tracker_.getReliableTracks();
306-
auto tracks = convert_tracks(rv_tracks, chunk.category, timestamp);
282+
auto tracks = convert_tracks(rv_tracks, chunk.category);
307283

308284
LOG_DEBUG("Processed chunk for {}/{}: {} detections -> {} reliable tracks", scope_.scene_id,
309285
scope_.category,

tracker/test/unit/track_publisher_test.cpp

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -357,48 +357,5 @@ TEST_F(TrackPublisherTest, Serialize_Track_WithoutConfidence_OmitsConfidenceFiel
357357
EXPECT_FALSE(doc["objects"][0].HasMember("confidence"));
358358
}
359359

360-
TEST_F(TrackPublisherTest, Serialize_Track_WithFirstSeen_EmitsFirstSeenField) {
361-
auto mock_client = std::make_shared<MockMqttClient>();
362-
TrackPublisher publisher(mock_client);
363-
364-
std::string captured_payload;
365-
EXPECT_CALL(*mock_client, isConnected()).WillOnce(Return(true));
366-
EXPECT_CALL(*mock_client, publish(_, _))
367-
.WillOnce([&captured_payload](const std::string&, const std::string& payload) {
368-
captured_payload = payload;
369-
});
370-
371-
Track track = createSampleTrack("a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "person");
372-
track.first_seen_iso = "2026-01-27T11:00:00.000Z";
373-
publisher.publish("scene-1", "Scene", "person", "2026-01-27T12:00:00.000Z", {track}, {});
374-
375-
rapidjson::Document doc;
376-
ASSERT_FALSE(doc.Parse(captured_payload.c_str()).HasParseError());
377-
378-
ASSERT_TRUE(doc["objects"][0].HasMember("first_seen"));
379-
EXPECT_STREQ(doc["objects"][0]["first_seen"].GetString(), "2026-01-27T11:00:00.000Z");
380-
}
381-
382-
TEST_F(TrackPublisherTest, Serialize_Track_WithoutFirstSeen_OmitsFirstSeenField) {
383-
auto mock_client = std::make_shared<MockMqttClient>();
384-
TrackPublisher publisher(mock_client);
385-
386-
std::string captured_payload;
387-
EXPECT_CALL(*mock_client, isConnected()).WillOnce(Return(true));
388-
EXPECT_CALL(*mock_client, publish(_, _))
389-
.WillOnce([&captured_payload](const std::string&, const std::string& payload) {
390-
captured_payload = payload;
391-
});
392-
393-
Track track = createSampleTrack("a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "person");
394-
// first_seen_iso is empty (default-constructed)
395-
publisher.publish("scene-1", "Scene", "person", "2026-01-27T12:00:00.000Z", {track}, {});
396-
397-
rapidjson::Document doc;
398-
ASSERT_FALSE(doc.Parse(captured_payload.c_str()).HasParseError());
399-
400-
EXPECT_FALSE(doc["objects"][0].HasMember("first_seen"));
401-
}
402-
403360
} // namespace
404361
} // namespace tracker

tracker/test/unit/tracking_worker_test.cpp

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -605,76 +605,5 @@ TEST_F(TrackingWorkerTest, Tracking_Confidence_PreservedThroughTracker) {
605605
}
606606
}
607607

608-
// Test that first_seen is set on the first observation of a track and remains stable
609-
// across subsequent observations (same RobotVision ID → same UUID → same first_seen).
610-
TEST_F(TrackingWorkerTest, Tracking_FirstSeen_SetOnFirstObservationAndStable) {
611-
std::vector<Track> all_published_tracks;
612-
std::mutex mtx;
613-
std::condition_variable cv;
614-
int callback_count = 0;
615-
const int kChunksToSend = 3;
616-
617-
PublishCallback callback = [&](const std::string&, const std::string&, const std::string&,
618-
const std::string&, const std::vector<Track>& tracks,
619-
const std::unordered_map<std::string, double>&) {
620-
std::lock_guard lock(mtx);
621-
all_published_tracks.insert(all_published_tracks.end(), tracks.begin(), tracks.end());
622-
callback_count++;
623-
cv.notify_one();
624-
};
625-
626-
TrackingConfig config = make_test_tracking_config();
627-
config.max_unreliable_time_s = 0.0;
628-
629-
TrackingScope scope{"scene-1", "person"};
630-
TrackingWorker worker(scope, "Test Scene", 10, callback, config, cameras_);
631-
632-
for (int i = 0; i < kChunksToSend; ++i) {
633-
Chunk chunk;
634-
chunk.scene_id = "scene-1";
635-
chunk.category = "person";
636-
chunk.chunk_time = std::chrono::steady_clock::now();
637-
638-
DetectionBatch batch;
639-
batch.camera_id = "cam-1";
640-
batch.timestamp_iso = std::format("2026-01-27T12:00:{:02d}.000Z", i);
641-
batch.timestamp = std::chrono::system_clock::now();
642-
643-
Detection det;
644-
det.id = 1;
645-
det.bounding_box_px = cv::Rect2f(100.0f, 200.0f, 50.0f, 100.0f);
646-
batch.detections.push_back(std::move(det));
647-
648-
chunk.camera_batches.push_back(std::move(batch));
649-
worker.try_enqueue(std::move(chunk));
650-
}
651-
652-
{
653-
std::unique_lock lock(mtx);
654-
ASSERT_TRUE(cv.wait_for(lock, std::chrono::seconds(2),
655-
[&] { return callback_count >= kChunksToSend; }))
656-
<< "Timed out waiting for " << kChunksToSend << " publish callbacks";
657-
}
658-
659-
ASSERT_GT(all_published_tracks.size(), 0u)
660-
<< "No reliable tracks published";
661-
662-
// All tracks should have a non-empty first_seen
663-
for (const auto& track : all_published_tracks) {
664-
EXPECT_FALSE(track.first_seen_iso.empty())
665-
<< "Track " << track.id << " is missing first_seen";
666-
}
667-
668-
// Tracks with the same ID must have the same first_seen (stable across frames)
669-
std::unordered_map<std::string, std::string> id_to_first_seen;
670-
for (const auto& track : all_published_tracks) {
671-
auto [it, inserted] = id_to_first_seen.emplace(track.id, track.first_seen_iso);
672-
if (!inserted) {
673-
EXPECT_EQ(it->second, track.first_seen_iso)
674-
<< "Track " << track.id << " has inconsistent first_seen across frames";
675-
}
676-
}
677-
}
678-
679608
} // namespace
680609
} // namespace tracker

0 commit comments

Comments
 (0)