diff --git a/sorteerhoed/central_management.py b/sorteerhoed/central_management.py index 5cbb425..8c788b0 100644 --- a/sorteerhoed/central_management.py +++ b/sorteerhoed/central_management.py @@ -295,6 +295,17 @@ class CentralManagement(): self.currentHit.open_page_at = datetime.datetime.utcnow() self.store.saveHIT(self.currentHit) self.setLight(True) + elif signal.name == 'server.close': + if not signal.params['abandoned']: + continue + a = self.currentHit.getLastAssignment() + if a.assignment_id != signal.params['assignment_id']: + self.logger.info(f"Close of older assignment_id: {signal}") + continue + self.logger.critical(f"Websocket closed of active assignment_id: {signal}") + a.abandoned_at = datetime.datetime.utcnow() + self.store.saveAssignment(a) + self.plotter.park() elif signal.name == 'assignment.submit': a = self.currentHit.getLastAssignment() if a.assignment_id != signal.params['assignment_id']: diff --git a/sorteerhoed/webserver.py b/sorteerhoed/webserver.py index 20de705..14b691d 100644 --- a/sorteerhoed/webserver.py +++ b/sorteerhoed/webserver.py @@ -56,6 +56,8 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): self.plotterQ = plotterQ self.eventQ = eventQ self.store = store + self.assignment_id = None + self.abandoned = False def check_origin(self, origin): parsed_origin = urlparse(origin) @@ -79,7 +81,8 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): if self.assignment.assignment_id != self.assignment_id: raise Exception(f"Opening websocket for invalid assignment {self.assignment_id}") - self.timeout = datetime.datetime.now() + datetime.timedelta(seconds=self.store.getHitTimeout()) + self.timeout = self.assignment.created_at + datetime.timedelta(seconds=self.store.getHitTimeout()) +# timeLeft = (self.timeout - datetime.datetime.utcnow()).total_seconds() if self.hit.isSubmitted(): raise Exception("Opening websocket for already submitted hit") @@ -87,7 +90,8 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): #logger.info(f"New client connected: {self.request.remote_ip} for {self.hit.id}/{self.hit.hit_id}") self.eventQ.put(Signal('server.open', dict(assignment_id=self.assignment_id))) self.strokes = [] - + + # the client sent the message def on_message(self, message): @@ -97,7 +101,7 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): logger.critical(f"Skip message for non-last assignment {message}") return - if datetime.datetime.now() > self.timeout: + if datetime.datetime.utcnow() > self.timeout: logger.critical("Close websocket after timeout (abandon?)") self.close() return @@ -164,6 +168,9 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): # client disconnected def on_close(self): self.__class__.rmConnection(self) + if self.assignment_id: + self.eventQ.put(Signal('server.close', dict(assignment_id=self.assignment_id, abandoned=self.abandoned))) + logger.info(f"Client disconnected: {self.request.remote_ip}") # TODO: abandon assignment?? @@ -206,6 +213,20 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler): if client not in cls.connections: return cls.connections.remove(client) + + @classmethod + def hasConnection(cls, client): + return client in cls.connections + + + @classmethod + def timeoutConnectionForAssignment(cls, assignment_id): + logger.warn(f"Check timeout for {assignment_id}") + for client in cls.connections: + logger.info(client.assignment_id) + if client.assignment_id == assignment_id: + client.abandoned = True + client.close() class StatusWebSocketHandler(tornado.websocket.WebSocketHandler): @@ -311,6 +332,8 @@ class DrawPageHandler(tornado.web.RequestHandler): logger.warning(f"Create new assignment {assignmentId}") assignment = self.store.newAssignment(self.store.currentHit, assignmentId) self.store.saveAssignment(assignment) + logger.info(f"Set close timeout for {self.store.getHitTimeout()}") + Server.loop.asyncio_loop.call_later(self.store.getHitTimeout(), WebSocketHandler.timeoutConnectionForAssignment, assignment.assignment_id) previous_hit = self.store.getLastSubmittedHit() if not previous_hit: @@ -432,7 +455,6 @@ class StatusPage(): return [hit.toDict() for hit in hits] - class Server: """ Server for HIT -> plotter events diff --git a/www/basic.svg b/www/basic.svg index 315d9e1..2e5c7a4 100644 --- a/www/basic.svg +++ b/www/basic.svg @@ -5,11 +5,11 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" - id="svg8" - version="1.1" - viewBox="0 0 210 210" + width="210mm" height="210mm" - width="210mm"> + viewBox="0 0 210 210" + version="1.1" + id="svg8"> + style="stroke-width:1.52441633" + id="layer1" + transform="matrix(0.65598879,0,0,0.65598879,44.11553,-86.509667)"> + width="107.34524" + height="107.34524" + x="51.327381" + y="138.32738" /> diff --git a/www/basic_square.svg b/www/basic_square.svg index 20c8df1..a300851 100644 --- a/www/basic_square.svg +++ b/www/basic_square.svg @@ -13,7 +13,7 @@ height="180mm" preserveAspectRatio="none" id="svg3" - sodipodi:docname="000139.svg" + sodipodi:docname="basic_square.svg" inkscape:version="0.92.4 (5da689c313, 2019-01-14)"> @@ -37,22 +37,23 @@ guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:window-width="3836" - inkscape:window-height="2126" + inkscape:window-width="3840" + inkscape:window-height="2160" id="namedview5" showgrid="false" inkscape:zoom="1.3875926" - inkscape:cx="311.32047" + inkscape:cx="-14.783892" inkscape:cy="589.76197" - inkscape:window-x="2" - inkscape:window-y="32" + inkscape:window-x="0" + inkscape:window-y="0" inkscape:window-maximized="1" - inkscape:current-layer="svg3" /> + inkscape:current-layer="svg3" + showguides="false" /> + y="165.88982" />