-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add scene programs state manager
- Loading branch information
1 parent
be1ce76
commit 83386df
Showing
2 changed files
with
166 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# listens to activity on a scene and kills programs when inactive | ||
from arena import * | ||
from scenestate import SceneState | ||
|
||
DFT_INACTIVE_TIME_SEC = 5 | ||
DFT_SCENE_STATE_UPDATE_SEC = 60 | ||
|
||
def scene_inactive(scene): | ||
|
||
if not scene: | ||
raise ValueError("Scene not set; Did you call SceneState's set_scene()?") | ||
return | ||
|
||
programs = [v for k,v in scene.all_objects.items() if hasattr(v, "type") and v.type == "program"] | ||
|
||
for prgrm in programs: | ||
try: | ||
if prgrm.data.parent: # delete programs setup to run in a runtime | ||
print(f"DELETING: {prgrm.data.name} ({prgrm.object_id})") | ||
scene.delete_program(prgrm) | ||
except ValueError: | ||
pass | ||
except AttributeError: | ||
pass | ||
|
||
def main(state_update_sec, inactive_time_sec): | ||
|
||
# call inactive_callback after detecting the scene is inactive for | ||
# inactive_time_secs; program will exit afterwards (exit_on_inactive=True) | ||
scene_state = SceneState( | ||
inactive_callback=scene_inactive, | ||
inactive_time_secs=inactive_time_sec, | ||
exit_on_inactive=True) | ||
|
||
# create scene; user join/left callbacks handled by corresponding SceneState methods | ||
scene = Scene(..., | ||
user_join_callback=lambda s, c, m : scene_state.user_joined(c), | ||
user_left_callback=lambda s, c, m : scene_state.user_left(c.object_id)) | ||
|
||
scene_state.set_scene(scene) | ||
|
||
# update scene state every scene_state_update_sec | ||
# or inactive_time_sec / 2, if smaller; never faster than every second | ||
update_interval_sec = max(min(state_update_sec, inactive_time_sec / 2), 1) | ||
print(f"Updating scene state every {update_interval_sec} seconds") | ||
scene.run_forever(scene_state.update, update_interval_sec * 1000) | ||
scene.run_tasks() | ||
|
||
if __name__ == "__main__": | ||
""" Parse arguments; start main """ | ||
|
||
parser = argparse.ArgumentParser() | ||
parser.add_argument( | ||
"--state_update_sec", default=os.environ.get('SCENE_STATE_UPDATE_MS', DFT_SCENE_STATE_UPDATE_SEC), | ||
help="Specify the scene state update interval in seconds (can also be specified using SCENE_STATE_UPDATE_MS environment variable)") | ||
parser.add_argument( | ||
"--inactive_time_sec", default=os.environ.get('INACTIVE_TIME_SEC', DFT_INACTIVE_TIME_SEC), | ||
help="Specify the scene inactivity time until a scene is said to be inactive in seconds (can also be specified using INACTIVE_TIME_SEC environment variable)") | ||
args=parser.parse_args() | ||
|
||
try: | ||
main(**vars(args)) | ||
except Exception as e: | ||
print(str(e)) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import time | ||
import sys | ||
from datetime import datetime | ||
|
||
class SceneState(): | ||
|
||
DFT_INACTIVE_TIME_SECS=3600 | ||
|
||
def __init__(self, scene=None, inactive_callback=None, inactive_time_secs=DFT_INACTIVE_TIME_SECS, active_on_new_users=False, exit_on_inactive=True): | ||
self.scene = scene | ||
self.inactive_callback = inactive_callback | ||
self.inactive_time_secs = inactive_time_secs | ||
self.users_cam = dict() | ||
self._active = True | ||
self._active_ts = datetime.now() | ||
self.active_on_new_users = active_on_new_users | ||
self.exit_on_inactive = exit_on_inactive | ||
|
||
@property | ||
def active(self): | ||
return self._active | ||
|
||
@active.setter | ||
def active(self, value): | ||
self._active = value | ||
self._active_ts = datetime.now() | ||
|
||
def set_scene(self, scene): | ||
self.scene = scene | ||
|
||
def user_joined(self, camera): | ||
user_cam = UserCameraState(camera) | ||
self.users_cam[user_cam.id] = user_cam | ||
if self.active_on_new_users: self.active = True | ||
|
||
def user_left(self, id): | ||
user_cam = self.users_cam[id] | ||
self.users_cam.pop(user_cam.id) | ||
return user_cam | ||
|
||
def list_users(self): | ||
return self.users_cam.items() | ||
|
||
def update(self): | ||
active_users = False | ||
for id, user_cam in self.users_cam.items(): | ||
active_users = active_users or user_cam.update_state() | ||
|
||
if self.active: | ||
if active_users: self.active = True | ||
|
||
dif = (datetime.now() - self._active_ts).total_seconds() | ||
if dif > self.inactive_time_secs: | ||
self.active = False | ||
if self.inactive_callback: self.inactive_callback(self.scene) | ||
else: | ||
if self.exit_on_inactive: | ||
if self.scene: self.scene.exit() | ||
else: sys.exit(0) | ||
|
||
|
||
def scene_inactive_callback(scene): | ||
print("Inactive!") | ||
if not scene: return | ||
print() | ||
|
||
class UserCameraState: | ||
|
||
DFT_INACTIVE_TIME_SECS=600 | ||
|
||
def __init__(self, camera, inactive_time_secs=DFT_INACTIVE_TIME_SECS): | ||
self.id = camera.object_id | ||
self.camera = camera | ||
self.prev_pos = None | ||
self.active = True | ||
self.active_ts = datetime.now() | ||
self.inactive_time_secs = inactive_time_secs | ||
|
||
def curr_pos(self): | ||
return self.camera.data.position | ||
|
||
def moved(self, min_displacement=0.5, update_pos=True): | ||
displacement = 0 | ||
if self.prev_pos: | ||
displacement = self.prev_pos.distance_to(self.curr_pos()) | ||
if (update_pos): self.prev_pos = self.curr_pos() | ||
if (displacement > min_displacement): return True | ||
return False | ||
|
||
def update_state(self): | ||
if self.moved(): | ||
self.active = True | ||
self.active_ts = datetime.now() | ||
|
||
if (self.active): | ||
dif = (datetime.now() - self.active_ts).total_seconds() | ||
if dif > self.inactive_time_secs: | ||
self.active = False | ||
|
||
return self.active | ||
|