Skip to content

ITEP-23442: Include dwell time in region updates topic#1300

Draft
saratpoluri wants to merge 1 commit intomainfrom
feature/dwell-analytics
Draft

ITEP-23442: Include dwell time in region updates topic#1300
saratpoluri wants to merge 1 commit intomainfrom
feature/dwell-analytics

Conversation

@saratpoluri
Copy link
Copy Markdown
Contributor

📝 Description

Provide a clear summary of the changes and the context behind them. Describe what was changed, why it was needed, and how the changes address the issue or add value.

✨ Type of Change

Select the type of change your PR introduces:

  • 🐞 Bug fix – Non-breaking change which fixes an issue
  • 🚀 New feature – Non-breaking change which adds functionality
  • 🔨 Refactor – Non-breaking change which refactors the code base
  • 💥 Breaking change – Changes that break existing functionality
  • 📚 Documentation update
  • 🔒 Security update
  • 🧪 Tests
  • 🚂 CI

🧪 Testing Scenarios

Describe how the changes were tested and how reviewers can test them too:

  • ✅ Tested manually
  • 🤖 Ran automated end-to-end tests

✅ Checklist

Before submitting the PR, ensure the following:

  • 🔍 PR title is clear and descriptive
  • 📝 For internal contributors: If applicable, include the JIRA ticket number (e.g., ITEP-123456) in the PR title. Do not include full URLs
  • 💬 I have commented my code, especially in hard-to-understand areas
  • 📄 I have made corresponding changes to the documentation
  • ✅ I have added tests that prove my fix is effective or my feature works

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds “live region dwell time” to region-scoped object outputs (region data + region events) so consumers can see how long an object has been inside a region at publish time.

Changes:

  • Extend detections serialization to optionally include regions.<region_id>.dwell (seconds since entered).
  • Update Scene Controller region/region-event publishing paths to request region dwell.
  • Update documentation and unit tests to cover/describe the new dwell behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
controller/src/controller/detections_builder.py Adds optional region dwell computation during object serialization.
controller/src/controller/scene_controller.py Enables dwell inclusion for region detections and region event payload object lists.
tests/sscape_tests/scenescape/test_detections_builder.py Updates timestamp format expectations and adds a unit test for dwell inclusion.
docs/user-guide/microservices/controller/data_formats.md Documents live region dwell field in region-scoped payloads.
docs/user-guide/microservices/controller/_assets/scene-controller-api.yaml Updates AsyncAPI text to mention regions.<region_id>.dwell in region payloads.

Comment on lines +384 to +395
| Field | Type | Description |
| ------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `timestamp` | string (ISO 8601 UTC) | Event timestamp |
| `scene_id` | string | Scene identifier (UUID) |
| `scene_name` | string | Scene name |
| `region_id` | string | Region identifier (UUID) |
| `region_name` | string | Region name |
| `counts` | object | Map of category to object count currently inside the region (e.g. `{"person": 2}`) |
| `objects` | array | Tracked objects currently inside the region. Each object includes live `regions.<region_name>.dwell` in addition to [Common Output Track Fields](#common-output-track-fields) |
| `entered` | array | Objects that entered the region during this cycle; each element is a bare track object and may include live `regions.<region_name>.dwell`. Empty when no entry occurred |
| `exited` | array | Objects that exited the region during this cycle; each element is `{"object": <track>, "dwell": <seconds>}`. Empty when no exit occurred |
| `metadata` | object | Region geometry: `title`, `uuid`, `points` (polygon vertices in metres), `area` (`"poly"`), `fromSensor` (boolean) |
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Region Event docs describe the live dwell field as regions.<region_name>.dwell, but the regions map is keyed by region UUID/ID (see example above where regions keys are UUIDs). This should refer to regions.<region_id>.dwell to match actual payload structure and avoid confusing consumers.

Suggested change
| Field | Type | Description |
| ------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `timestamp` | string (ISO 8601 UTC) | Event timestamp |
| `scene_id` | string | Scene identifier (UUID) |
| `scene_name` | string | Scene name |
| `region_id` | string | Region identifier (UUID) |
| `region_name` | string | Region name |
| `counts` | object | Map of category to object count currently inside the region (e.g. `{"person": 2}`) |
| `objects` | array | Tracked objects currently inside the region. Each object includes live `regions.<region_name>.dwell` in addition to [Common Output Track Fields](#common-output-track-fields) |
| `entered` | array | Objects that entered the region during this cycle; each element is a bare track object and may include live `regions.<region_name>.dwell`. Empty when no entry occurred |
| `exited` | array | Objects that exited the region during this cycle; each element is `{"object": <track>, "dwell": <seconds>}`. Empty when no exit occurred |
| `metadata` | object | Region geometry: `title`, `uuid`, `points` (polygon vertices in metres), `area` (`"poly"`), `fromSensor` (boolean) |
| Field | Type | Description |
| ------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `timestamp` | string (ISO 8601 UTC) | Event timestamp |
| `scene_id` | string | Scene identifier (UUID) |
| `scene_name` | string | Scene name |
| `region_id` | string | Region identifier (UUID) |
| `region_name` | string | Region name |
| `counts` | object | Map of category to object count currently inside the region (e.g. `{"person": 2}`) |
| `objects` | array | Tracked objects currently inside the region. Each object includes live `regions.<region_id>.dwell` in addition to [Common Output Track Fields](#common-output-track-fields) |
| `entered` | array | Objects that entered the region during this cycle; each element is a bare track object and may include live `regions.<region_id>.dwell`. Empty when no entry occurred |
| `exited` | array | Objects that exited the region during this cycle; each element is `{"object": <track>, "dwell": <seconds>}`. Empty when no exit occurred |
| `metadata` | object | Region geometry: `title`, `uuid`, `points` (polygon vertices in metres), `area` (`"poly"`), `fromSensor` (boolean) |

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +35
def _build_region_output(regions, include_region_dwell, current_time):
serialized_regions = {}
for region_name, region_data in regions.items():
serialized_region = dict(region_data)
if include_region_dwell and 'entered' in region_data:
entered = get_epoch_time(region_data['entered'])
serialized_region['dwell'] = current_time - entered
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Computing dwell currently calls get_epoch_time() (which uses datetime.strptime) for every region on every serialized object. In region-scoped outputs this can become a hot path with many objects/regions per frame. Consider caching an epoch form of entered in chain_data.regions at entry time (or storing entered_epoch alongside the ISO string) so dwell can be computed without repeated string parsing.

Suggested change
def _build_region_output(regions, include_region_dwell, current_time):
serialized_regions = {}
for region_name, region_data in regions.items():
serialized_region = dict(region_data)
if include_region_dwell and 'entered' in region_data:
entered = get_epoch_time(region_data['entered'])
serialized_region['dwell'] = current_time - entered
def _get_region_entered_epoch(region_data):
entered_epoch = region_data.get('entered_epoch')
if entered_epoch is None and 'entered' in region_data:
entered_epoch = get_epoch_time(region_data['entered'])
region_data['entered_epoch'] = entered_epoch
return entered_epoch
def _build_region_output(regions, include_region_dwell, current_time):
serialized_regions = {}
for region_name, region_data in regions.items():
serialized_region = dict(region_data)
serialized_region.pop('entered_epoch', None)
if include_region_dwell and 'entered' in region_data:
entered_epoch = _get_region_entered_epoch(region_data)
serialized_region['dwell'] = current_time - entered_epoch

Copilot uses AI. Check for mistakes.
Comment on lines 278 to +287
def publishRegionDetections(self, scene, objects, otype, jdata):
for rname in scene.regions:
robjects = []
for obj in objects:
if rname in obj.chain_data.regions:
robjects.append(obj)
# Region-specific detections: include sensor data
jdata['objects'] = buildDetectionsList(robjects, scene, False, include_sensors=True)
jdata['objects'] = buildDetectionsList(
robjects, scene, False, include_sensors=True,
include_region_dwell=True, current_time=get_epoch_time(jdata['timestamp']))
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_epoch_time(jdata['timestamp']) is computed inside the loop over scene.regions. Since jdata['timestamp'] is the same for all regions in this publish cycle, this does repeated datetime.strptime work. Consider computing the epoch timestamp once before the loop (or passing an already-parsed epoch timestamp into this method) and reusing it for each buildDetectionsList call.

Copilot uses AI. Check for mistakes.
Comment on lines 104 to +108
if len(chain_data.regions):
obj_dict['regions'] = chain_data.regions
if include_region_dwell:
if current_time is None:
current_time = get_epoch_time()
obj_dict['regions'] = _build_region_output(chain_data.regions, include_region_dwell, current_time)
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When include_region_dwell is enabled and current_time is not provided, current_time is set inside prepareObjDict. Because prepareObjDict is called once per object, different objects in the same serialized list/dict can end up with slightly different dwell values, making a single payload internally inconsistent. Consider determining current_time once in buildDetectionsList / buildDetectionsDict (or in the calling site) and passing it through for all objects.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants