Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
1109c30
Implement singleton sensor tagging for tracked objects
rawatts10 Feb 11, 2026
d38b94e
Fix singleton sensor event publishing and cache resilience
rawatts10 Feb 17, 2026
93761c0
Fix type consistency in environmental sensor value comparisons
rawatts10 Feb 18, 2026
532562d
Fix issues raised by Copilot review
rawatts10 Feb 18, 2026
3da5a2c
Fix trailing whitespace linting errors
rawatts10 Feb 18, 2026
58516fe
Modifications to cleanup code
saratpoluri Feb 18, 2026
e9a557b
Apply suggestions from code review
saratpoluri Feb 18, 2026
d012fd1
Protect dicts used in different threads to avoid race conditions
saratpoluri Feb 18, 2026
6af611f
Merge branch 'main' into fix/singleton-sensor-tagging
saratpoluri Feb 18, 2026
e088838
Remove exposure calculation from singleton sensor tagging
rawatts10 Feb 18, 2026
56076b9
Add unit tests for cache manager
saratpoluri Feb 18, 2026
17b12cb
Add functional test for singleton sensor tagging
rawatts10 Feb 18, 2026
0b602a0
Revert "Add functional test for singleton sensor tagging"
saratpoluri Feb 21, 2026
d465e5f
Merge branch 'main' into fix/singleton-sensor-tagging
saratpoluri Feb 21, 2026
69284d1
Remove whitespaces
saratpoluri Feb 21, 2026
0687112
Update controller/src/controller/detections_builder.py
saratpoluri Feb 21, 2026
0a17d9e
Update controller/src/controller/scene.py
saratpoluri Feb 21, 2026
3444c53
Merge branch 'main' into fix/singleton-sensor-tagging
saratpoluri Mar 31, 2026
fce470c
Fix copilot highlighted errors
saratpoluri Mar 31, 2026
f8e5539
Add sensor values to regions dict
saratpoluri Mar 31, 2026
29d07a0
Merge branch 'main' into fix/singleton-sensor-tagging
saratpoluri Mar 31, 2026
52a7dd3
Address additional code review comments
saratpoluri Mar 31, 2026
2fe1eb8
Merge branch 'main' into fix/singleton-sensor-tagging
saratpoluri Mar 31, 2026
a67d83c
Add more unit testing
saratpoluri Mar 31, 2026
e36622e
Improve test instruction adherence
saratpoluri Mar 31, 2026
8b381dc
Make scene unit tests comprehensive
saratpoluri Mar 31, 2026
e0ca161
Separate django unit and logic unit tests
saratpoluri Mar 31, 2026
f6e541a
Cover new sensor behavior with functional test
saratpoluri Apr 1, 2026
a22089b
[DOCS] Generate API docs for Scene Controllers (#1248)
sgolebiewski-intel Apr 1, 2026
45d301f
Tracker Conan: Update dependency cpp-httplib to v0.39.0 (#1250)
oep-renovate[bot] Apr 1, 2026
a97309b
Tracker Conan: Update tracker conan patch updates (#1249)
oep-renovate[bot] Apr 1, 2026
d738628
[Renovate Config] Remove schedule override for patch updates (#1256)
dmytroye Apr 1, 2026
d2c5b89
GitHub Actions: Bump the github-actions-dependency group with 5 updat…
dependabot[bot] Apr 2, 2026
4cbe87f
Added release 2026.0 to weekly tests (#1275)
Irakus Apr 2, 2026
79dc82a
pip: bump pybind11 from 3.0.2 to 3.0.3 in /.github/resources (#1261)
dependabot[bot] Apr 2, 2026
aaafff0
Revert Kubernetes package version (#1277)
Irakus Apr 2, 2026
78dfcf6
[DLStreamer] Update to 2026.1.0-20260331-weekly-ubuntu24 (#1276)
scenescapecicd Apr 2, 2026
5708bc4
Prettier write fixes
saratpoluri Apr 2, 2026
16bfb1d
Address review comments
saratpoluri Apr 2, 2026
e746441
Remove redundancy
saratpoluri Apr 2, 2026
ad6c6f7
Merge branch 'main' into fix/singleton-sensor-tagging
saratpoluri Apr 2, 2026
2f97fcc
Fix deserialization of tracked objects
saratpoluri Apr 2, 2026
fee53b5
Fix debounce region cleaup logic
saratpoluri Apr 2, 2026
0d8d74f
Update instructions to rebuild images after changes before testing
saratpoluri Apr 2, 2026
ce7e571
Merge branch 'main' into fix/singleton-sensor-tagging
saratpoluri Apr 2, 2026
623725a
Fix prettier-check
saratpoluri Apr 2, 2026
9ead4be
Refactor
saratpoluri Apr 2, 2026
b37427b
Address code review comments
saratpoluri Apr 3, 2026
d11beea
Merge branch 'main' into fix/singleton-sensor-tagging
saratpoluri Apr 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions controller/src/controller/cache_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,33 @@ def refreshScenes(self):

uid = scene_data['uid']
if uid not in self.cached_scenes_by_uid:
# Creating new scene - check if there was an old scene with sensor cache
old_scene = self._old_scene_cache.get(uid) if hasattr(self, '_old_scene_cache') and self._old_scene_cache else None
scene = Scene.deserialize(scene_data)

# Restore sensor cache from old scene if it existed (after scene invalidation/recreation)
# Note: Sensors without cached values (never received data) will naturally skip restoration
# since hasattr checks will return False
if old_scene and hasattr(old_scene, 'sensors'):
restored_count = 0
for sensor_id, old_sensor in old_scene.sensors.items():
if sensor_id in scene.sensors:
new_sensor = scene.sensors[sensor_id]
# Preserve sensor cache values if they exist
restored = False
if hasattr(old_sensor, 'value'):
new_sensor.value = old_sensor.value
restored = True
if hasattr(old_sensor, 'lastValue'):
new_sensor.lastValue = old_sensor.lastValue
restored = True
if hasattr(old_sensor, 'lastWhen'):
new_sensor.lastWhen = old_sensor.lastWhen
restored = True
if restored:
restored_count += 1
if restored_count > 0:
log.debug(f"Restored sensor cache for {restored_count} sensor(s) in scene {uid}")
Comment thread
saratpoluri marked this conversation as resolved.
Outdated
else:
scene = self.cached_scenes_by_uid[uid]
scene.updateScene(scene_data)
Expand All @@ -70,6 +96,11 @@ def refreshScenes(self):
for sensorID in scene.sensors.keys():
self._cached_scenes_by_sensorID[sensorID] = scene
self.cached_scenes_by_uid[scene.uid] = scene

# Clear old scene cache after processing all scenes
if hasattr(self, '_old_scene_cache'):
self._old_scene_cache = None

self._cache_refreshed = get_epoch_time()
return

Expand Down Expand Up @@ -185,6 +216,8 @@ def sceneWithRemoteChildID(self, childID):
return self.cached_child_transforms_by_uid.get(childID, None)

def invalidate(self):
# Preserve old scene cache for sensor value restoration
self._old_scene_cache = self.cached_scenes_by_uid if hasattr(self, 'cached_scenes_by_uid') else {}
self.cached_scenes_by_uid = None
if not hasattr(self, 'cached_child_transforms_by_uid') or self.cached_child_transforms_by_uid is None:
self.cached_child_transforms_by_uid = {}
Expand Down
44 changes: 37 additions & 7 deletions controller/src/controller/detections_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@
from scene_common.timestamp import get_iso_time


def buildDetectionsDict(objects, scene):
def buildDetectionsDict(objects, scene, include_sensors=False):
result_dict = {}
for obj in objects:
obj_dict = prepareObjDict(scene, obj, False)
obj_dict = prepareObjDict(scene, obj, False, include_sensors)
result_dict[obj_dict['id']] = obj_dict
return result_dict

def buildDetectionsList(objects, scene, update_visibility=False):
def buildDetectionsList(objects, scene, update_visibility=False, include_sensors=False):
result_list = []
for obj in objects:
obj_dict = prepareObjDict(scene, obj, update_visibility)
obj_dict = prepareObjDict(scene, obj, update_visibility, include_sensors)
result_list.append(obj_dict)
return result_list

def prepareObjDict(scene, obj, update_visibility):
def prepareObjDict(scene, obj, update_visibility, include_sensors=False):
aobj = obj
if isinstance(obj, TripwireEvent):
aobj = obj.object
Expand Down Expand Up @@ -71,8 +71,38 @@ def prepareObjDict(scene, obj, update_visibility):
chain_data = aobj.chain_data
if len(chain_data.regions):
obj_dict['regions'] = chain_data.regions
if len(chain_data.sensors):
obj_dict['sensors'] = chain_data.sensors

# Build sensor output from new structure (only if include_sensors is True)
if include_sensors:
sensors_output = {}

# Environmental sensors: readings + exposure as structured object
for sensor_id, state in chain_data.env_sensor_state.items():
values = state['readings'] if 'readings' in state and state['readings'] else []

# Calculate total exposure (including current value if present)
exposure_total = state['exposure']['total']
if state['exposure']['last_value'] is not None and hasattr(scene, 'when'):
# Add exposure from last reading to now; clamp negative intervals to zero
dt = scene.when - state['exposure']['last_time']
if dt > 0:
exposure_total += state['exposure']['last_value'] * dt
Comment thread
saratpoluri marked this conversation as resolved.
Outdated

sensors_output[sensor_id] = {
'values': values,
'exposure': exposure_total
}

# Attribute sensors: events as structured object
for sensor_id, events in chain_data.attr_sensor_events.items():
if events:
sensors_output[sensor_id] = {
'values': events
}

if sensors_output:
obj_dict['sensors'] = sensors_output

if hasattr(aobj, 'confidence'):
obj_dict['confidence'] = aobj.confidence
if hasattr(aobj, 'similarity'):
Expand Down
10 changes: 6 additions & 4 deletions controller/src/controller/moving_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import datetime
import struct
import warnings
from dataclasses import dataclass
from dataclasses import dataclass, field
Comment thread
saratpoluri marked this conversation as resolved.
from threading import Lock
from typing import Dict, List

Expand All @@ -30,8 +30,10 @@
class ChainData:
regions: Dict
publishedLocations: List[Point]
sensors: Dict
persist: Dict
active_sensors: set = field(default_factory=set)
env_sensor_state: Dict = field(default_factory=dict) # {'sensor_id': {'last_reading': (ts, val), 'exposure': {...}}}
attr_sensor_events: Dict = field(default_factory=dict) # {'sensor_id': [(ts, val), ...]}
Comment thread
saratpoluri marked this conversation as resolved.

class Chronoloc:
def __init__(self, point: Point, when: datetime, bounds: Rectangle):
Expand Down Expand Up @@ -125,7 +127,7 @@ def _decodeReIDVector(self, reid):

def setPersistentAttributes(self, info, persist_attributes):
if self.chain_data is None:
self.chain_data = ChainData(regions={}, publishedLocations=[], sensors={}, persist={})
self.chain_data = ChainData(regions={}, publishedLocations=[], persist={})
for attribute in persist_attributes:
attr, sub_attrs = (list(attribute.items())[0] if isinstance(attribute, dict) else (attribute, None))
if attr in info:
Expand All @@ -141,7 +143,7 @@ def setPersistentAttributes(self, info, persist_attributes):

def setGID(self, gid):
if self.chain_data is None:
self.chain_data = ChainData(regions={}, publishedLocations=[], sensors={}, persist={})
self.chain_data = ChainData(regions={}, publishedLocations=[], persist={})
self.gid = gid
self.first_seen = self.when
return
Expand Down
Loading
Loading