Commit 3e7594e5 authored by david's avatar david
Browse files

Fix for preloading. #78

parent a9d38e79
......@@ -184,21 +184,23 @@ class EngineExecutor(Timer):
Primarily used for automations performed by the scheduler.
"""
_lock = None
timer_store: dict = {}
logger = logging.getLogger("AuraEngine")
initialized = None
timer_store = {}
parent_timer = None
child_timer = None
direct_exec = None
timer_id = None
timer_type = None
EVENT_ON_READY = "on_ready"
_lock = None
direct_exec: bool = None
parent_timer: Timer = None
child_timer: Timer = None
timer_id: str = None
timer_type: str = None
func = None
param = None
diff = None
dt = None
def __init__(self, timer_type="BASE", parent_timer=None, due_time=None, func=None, param=None):
def __init__(self, timer_type:str="BASE", parent_timer:Timer=None, due_time=None, func=None, param=None):
"""
Constructor
......@@ -209,7 +211,6 @@ class EngineExecutor(Timer):
func (function): The function to be called
param (object): Parameter passt to the function
"""
self.initialized = False
self._lock = Lock()
from src.engine import Engine
now_unix = Engine.engine_time()
......@@ -224,9 +225,8 @@ class EngineExecutor(Timer):
self.timer_type = timer_type
self.timer_id = f"{timer_type}:{func.__name__}:{due_time}"
if not due_time:
diff = 0
else:
diff = 0
if due_time:
diff = due_time - now_unix
self.diff = diff
......@@ -240,32 +240,22 @@ class EngineExecutor(Timer):
else:
if diff < 0:
msg = f"Timer '{self.timer_id}' is due in the past. Executing immediately ..."
self.logger.error(SU.red(msg))
self.logger.warn(SU.yellow(msg))
self.exec_now()
elif diff == 0:
self.logger.info(f"Timer '{self.timer_id}' to be executed immediately")
self.logger.debug(f"Timer '{self.timer_id}' to be executed immediately")
self.exec_now()
else:
self.logger.debug(f"Timer '{self.timer_id}' to be executed in default manner")
self.exec_timed()
self.start()
def on_ready(self, func):
"""
Calls the passed function `func` when the timer is ready.
"""
while self.initialized == False:
timer.sleep(0.001)
self.logger.info(SU.orange("Waiting until the EngineExecutor is done with initialization..."))
if not self.direct_exec: #TODO Evaluate if we should join for direct exec too
self.join()
func()
def wait_for_parent(self):
"""
Child timers are dependend on their parents. So let's wait until parents are done with their stuff.
@private
Child timers are dependend on their parents. So let's wait until parents are done with their stuff => finished execution.
Checks the parent state to be finished every 0.2 seconds.
"""
if self.parent_timer:
while self.parent_timer.is_alive():
......@@ -275,22 +265,27 @@ class EngineExecutor(Timer):
def exec_now(self):
"""
@private
Immediate execution within a thread. It's not stored in the timer store.
Assigns the `timer_id` as the thread name.
It also assigns the `timer_id` as the thread name.
"""
self.direct_exec = True
self.wait_for_parent()
thread = Thread(name=self.timer_id, target=self.func, args=(self.param,))
time.sleep(0.2)
thread.start()
self.initialized = True
def exec_timed(self):
"""
Timed execution in a thread.
@private
Assigns the `timer_id` as the thread name.
Timed execution in a thread. This method instroduces a slight delay to ensure
the thread is properly initialized before starting it.
It also assigns the `timer_id` as the thread name.
"""
def wrapper_func(param=None):
self.wait_for_parent()
......@@ -298,18 +293,22 @@ class EngineExecutor(Timer):
else: self.func()
super().__init__(self.diff, wrapper_func, (self.param,))
self._name = self.timer_id
self.initialized = True
time.sleep(0.2)
self.start()
def update_store(self):
"""
@private
Adds the instance to the store and cancels any previously existing commands.
If a timer with the given ID is already existing but also already executed,
then it is not added to the store. In such case the method returns `False`.
Returns:
(Boolean): True if the timer has been added to the store
(Boolean): True if the timer has been added to the store. False if the
timer is already existing but dead.
"""
with self._lock:
existing_command = None
......@@ -337,6 +336,8 @@ class EngineExecutor(Timer):
def is_alive(self):
"""
@private
Returns true if the command is still due to be executed.
"""
if self.direct_exec == True:
......@@ -346,7 +347,7 @@ class EngineExecutor(Timer):
def __str__(self):
"""
String represenation of the timer.
String representation of the timer.
"""
return f"[{self.timer_id}] exec at {str(self.dt)} (alive: {self.is_alive()})"
......@@ -372,7 +373,7 @@ class EngineExecutor(Timer):
@staticmethod
def log_commands():
"""
Prints a list of recent active and inactive timers.
Prints a list of recent active and inactive timers to the logger.
"""
msg = SU.blue("\n [ ENGINE COMMAND QUEUE ]\n")
EngineExecutor.remove_stale_timers()
......@@ -383,7 +384,7 @@ class EngineExecutor(Timer):
else:
for timer in timers:
if not timer.parent_timer:
line = f" => {str(timer)}\n"#
line = f" => {str(timer)}\n"
if timer.is_alive():
line = SU.green(line)
msg += line
......
......@@ -45,17 +45,16 @@ class AuraScheduler(threading.Thread):
- Executes engine actions in an automated fashion
"""
config = None
config:AuraConfig = None
logger = None
engine = None
exit_event = None
timeslot_renderer = None
programme = None
engine:Engine = None
exit_event:threading.Event = None
timeslot_renderer:TimeslotRenderer = None
programme:ProgrammeService = None
message_timer = []
fallback = None
is_initialized = None
is_initialized = None
is_initialized:bool = None
is_engine_ready:bool = None
def __init__(self, engine, fallback_manager):
......@@ -407,10 +406,11 @@ class TimeslotCommand(EngineExecutor):
"""
Command for triggering start and end of timeslot events.
"""
engine = None
config = None
engine: Engine = None
config: AuraConfig = None
def __init__(self, engine, timeslot):
def __init__(self, engine: Engine, timeslot):
"""
Constructor
......@@ -421,12 +421,15 @@ class TimeslotCommand(EngineExecutor):
self.config = AuraConfig()
self.engine = engine
now_unix = SU.timestamp()
fade_out_time = float(self.config.get("fade_out_time"))
start_fade_out = timeslot.end_unix - fade_out_time
self.logger.info(f"Fading out timeslot in {start_fade_out} seconds at {timeslot.timeslot_end} | Timeslot: {timeslot}")
# Initialize the "fade in" EngineExecuter and instatiate a connected child EngineExecuter for "fade out" when the parent is ready
start_fade_in = timeslot.start_unix - now_unix
start_fade_out = timeslot.end_unix - now_unix - fade_out_time
self.logger.debug(f"Fading in timeslot in {start_fade_in} seconds at {SU.fmt_time(timeslot.start_unix)} | Timeslot: {timeslot}")
self.logger.debug(f"Fading out timeslot in {start_fade_out} seconds at {SU.fmt_time(timeslot.end_unix - fade_out_time)} | Timeslot: {timeslot}")
# Initialize the "fade in" EngineExecuter and instantiate a connected child EngineExecuter for "fade out" when the parent is ready
super().__init__("TIMESLOT", None, timeslot.start_unix, self.do_start_timeslot, timeslot)
self.on_ready(lambda: EngineExecutor("TIMESLOT", self, start_fade_out, self.do_end_timeslot, timeslot))
EngineExecutor("TIMESLOT", self, timeslot.end_unix - fade_out_time, self.do_end_timeslot, timeslot)
def do_start_timeslot(self, timeslot):
......@@ -456,10 +459,11 @@ class PlayCommand(EngineExecutor):
"""
Command for triggering timed preloading and playing as a child command.
"""
engine = None
config = None
engine: Engine = None
config: AuraConfig = None
def __init__(self, engine, entries):
def __init__(self, engine: Engine, entries):
"""
Constructor
......@@ -470,11 +474,13 @@ class PlayCommand(EngineExecutor):
self.config = AuraConfig()
self.engine = engine
start_preload = entries[0].start_unix - self.config.get("preload_offset")
preload_offset = self.config.get("preload_offset")
start_preload = entries[0].start_unix - preload_offset
start_play = entries[0].start_unix
self.logger.debug(f"Preloading entries at {SU.fmt_time(start_preload)}, {preload_offset} seconds before playing it at {SU.fmt_time(start_play)}")
# Initialize the "preload" EngineExecuter and attach a child `PlayCommand` to the "on_ready" event handler
preload_timer = super().__init__("PRELOAD", None, start_preload, self.do_preload, entries)
self.on_ready(lambda: EngineExecutor("PLAY", self, start_play, self.do_play, entries))
super().__init__("PRELOAD", None, start_preload, self.do_preload, entries)
EngineExecutor("PLAY", self, start_play, self.do_play, entries)
def do_preload(self, entries):
......@@ -504,7 +510,7 @@ class PlayCommand(EngineExecutor):
# Let 'em play anyway ...
self.logger.critical(SU.red("PLAY: The entry/entries are not yet ready to be played (Entries: %s)" % ResourceUtil.get_entries_string(entries)))
while (entries[-1].status != EntryPlayState.READY):
self.logger.info("PLAY: Wait a little until preloading is done ...")
self.logger.info("PLAY: Wait a little bit until preloading is done ...")
time.sleep(2)
self.engine.player.play(entries[0], TransitionType.FADE)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment