diff --git a/hugvey/central_command.py b/hugvey/central_command.py index 7c2b749..203c8da 100644 --- a/hugvey/central_command.py +++ b/hugvey/central_command.py @@ -12,7 +12,7 @@ from zmq.asyncio import Context import asyncio from hugvey.communication import getTopic, zmqSend, zmqReceive, LOG_BS from hugvey.panopticon import Panopticon -from hugvey.story import Story +from hugvey.story import Story, Stopwatch from hugvey.speech.google import GoogleVoiceClient from hugvey.speech.player import Player from hugvey.speech.streamer import AudioStreamer @@ -23,8 +23,9 @@ import threading from hugvey.voice import VoiceStorage import multiprocessing from hugvey.speech.recorder import Recorder -from pythonosc import udp_client +from pythonosc import udp_client, osc_server, dispatcher import copy +from pythonosc.osc_server import AsyncIOOSCUDPServer mainLogger = logging.getLogger("hugvey") @@ -68,6 +69,8 @@ class CentralCommand(object): self.languageFiles = {} self.languageConfig = {} self.args = args # cli args + + self.timer = Stopwatch() eventLogger.addHandler(logging.handlers.QueueHandler(self.logQueue)) @@ -139,6 +142,7 @@ class CentralCommand(object): def getStatusSummary(self, selected_ids = []): status = { 'uptime': "-" if not self.start_time else (time.time() - self.start_time), + 'loop_timer': self.timer.getElapsed(), 'languages': self.config['languages'], 'hugvey_ids': self.hugvey_ids, 'hugveys': [], @@ -239,6 +243,33 @@ class CentralCommand(object): logger.warn('Stopping light sender') lightConn._sock.close() + + def restartTimerHandler(self, address, *args): + """ + See self.oscListener + """ + logger.warn(f"Restart loop timer") + self.timer.reset() + if len(args) > 0 and float(args[0]) > 0: + print(args, args[0]) + logger.warn(f"Set timer to custom time: {float(args[0])} seconds ago") + self.timer.setMark('start', time.time() - float(args[0])) + + async def oscListener(self): + """ + OSC server, listens for loop restarts + """ + dispatch = dispatcher.Dispatcher() + dispatch.map("/loop", self.restartTimerHandler) + + server = osc_server.AsyncIOOSCUDPServer( + ("0.0.0.0", 9000), dispatch, asyncio.get_event_loop() + ) + logger.info('Start OSC server to receive loop re-starts') +# await server.serve() + transport, protocol = await server.create_serve_endpoint() +# logger.critical(f"{transport}, {protocol}") +# transport.close() async def redLightController(self): """ @@ -384,6 +415,8 @@ class CentralCommand(object): self.catchException(self.commandSender())) self.tasks['lightSender'] = self.loop.create_task( self.catchException(self.lightSender())) + self.tasks['oscListener'] = self.loop.create_task( + self.catchException(self.oscListener())) self.tasks['redLightController'] = self.loop.create_task( self.catchException(self.redLightController())) @@ -680,6 +713,11 @@ class HugveyState(object): self.logger.log(LOG_BS, f"Send /hugvey {self.lightStatus}") self.command.commandLight('/hugvey', [self.lightId, self.lightStatus]) + + def transitionLight(self, intensity, duration): + self.lightIntensity = intensity + self.logger.log(LOG_BS, f"Send /hugvey_fade {self.lightIntensity} {duration}") + self.command.commandLight('/hugvey_fade', [self.lightId, intensity, duration]) def setLightId(self, id): """ diff --git a/hugvey/story.py b/hugvey/story.py index 8066e34..de80ea9 100644 --- a/hugvey/story.py +++ b/hugvey/story.py @@ -610,6 +610,8 @@ class Diversion(object): if type == 'timeout': self.method = self._divergeIfTimeout self.finaliseMethod = self._returnAfterTimeout + if type == 'collective_moment': + self.method = self._divergeIfCollectiveMoment if type == 'repeat': self.method = self._divergeIfRepeatRequest self.regex = re.compile(self.params['regex']) @@ -864,7 +866,39 @@ class Diversion(object): story.logger.info(f"Finalise diversion: {self.id}") # if self.params['returnAfterStrand']: # await story.setCurrentMessage(self.returnMessage) + async def _divergeIfCollectiveMoment(self, story, msgFrom, msgTo, direction): + """ + Central command timer times to a collective moment + """ + #: :var story: Story + if story.currentDiversion or not msgFrom or not msgTo: + return False + + if not msgTo.chapterStart: + # only when changing chapter + return + + window_open_second = float(self.params['start_minute']) * 60 + window_close_second = window_open_second + float(self.params['window']) + + now = story.hugvey.command.timer.getElapsed() + if now < window_open_second or now > window_close_second: + return + + #open! + msg = story.get(self.params['msgId']) + if msg is None: + story.logger.critical(f"Not a valid message id for diversion: {self.id} {self.params['msgId']}") + return + + self.returnMessage = msgTo + + self.createReturnDirectionsTo(story, msg, msgTo, direction, inheritTiming=True) + await story.setCurrentMessage(msg) + story.currentDiversion = self + return True + async def _divergeIfRepeatRequest(self, story, msgFrom, msgTo, direction): """ Participant asks if message can be repeated. @@ -1054,8 +1088,11 @@ class Stopwatch(object): self.paused_at = 0 self.isRunning.set() - def setMark(self, name): - self.marks[name] = time.time() + def setMark(self, name, overrideValue = None): + """ + Set a marker to current time. Or , if given, to any float one desires + """ + self.marks[name] = overrideValue if overrideValue else time.time() def hasMark(self, name): return name in self.marks diff --git a/www/index.html b/www/index.html index 068c001..dde0913 100644 --- a/www/index.html +++ b/www/index.html @@ -17,8 +17,8 @@
-
Uptime
-
{{uptime}}
+
Timer
+
{{loop_timer}} (up: {{uptime}})