11import asyncio
2+ import inspect
23import json
34import logging
45import queue
@@ -51,6 +52,9 @@ def __init__(self, *, rm_ref):
5152 self ._background_task_stopped = asyncio .Event ()
5253 self ._background_task_stopped .set ()
5354
55+ self ._callbacks = []
56+ self ._callbacks_async = []
57+
5458 @property
5559 def queues_set (self ):
5660 """
@@ -67,6 +71,22 @@ def text_buffer_uid(self):
6771 async def get_text_buffer (self , n_lines ):
6872 return await self ._RM .console_monitor .text (n_lines )
6973
74+ def subscribe (self , cb ):
75+ """
76+ Add a function or a coroutine to the list of callbacks. The callbacks must accept
77+ message as a parameter: cb(msg)
78+ """
79+ if inspect .iscoroutinefunction (cb ):
80+ self ._callbacks_async .append (cb )
81+ else :
82+ self ._callbacks .append (cb )
83+
84+ def unsubscribe (self , cb ):
85+ if inspect .iscoroutinefunction (cb ):
86+ self ._callbacks_async .remove (cb )
87+ else :
88+ self ._callbacks .remove (cb )
89+
7090 def get_new_msgs (self , last_msg_uid ):
7191 msg_list = []
7292 try :
@@ -94,6 +114,10 @@ async def _load_msgs_task(self):
94114 try :
95115 msg = await self ._RM .console_monitor .next_msg (timeout = 0.5 )
96116 self ._add_message (msg = msg )
117+ for cb in self ._callbacks :
118+ cb (msg )
119+ for cb in self ._callbacks_async :
120+ await cb (msg )
97121 except self ._RM .RequestTimeoutError :
98122 pass
99123 self ._background_task_stopped .set ()
@@ -167,3 +191,142 @@ def __init__(self, content_class, *args, **kwargs):
167191
168192 def __del__ (self ):
169193 del self ._content
194+
195+
196+ class ConsoleOutputStream :
197+ def __init__ (self , * , rm_ref ):
198+ self ._queues = {}
199+ self ._queue_max_size = 1000
200+
201+ @property
202+ def queues (self ):
203+ return self ._queues
204+
205+ def add_queue (self , key ):
206+ """
207+ Add a new queue to the dictionary of queues. The key is a reference to the socket for
208+ for connection with the client.
209+ """
210+ queue = asyncio .Queue (maxsize = self ._queue_max_size )
211+ self ._queues [key ] = queue
212+ return queue
213+
214+ def remove_queue (self , key ):
215+ """
216+ Remove the queue identified by the key from the dictionary of queues.
217+ """
218+ if key in self ._queues :
219+ del self ._queues [key ]
220+
221+ async def add_message (self , msg ):
222+ msg_json = json .dumps (msg )
223+ for q in self ._queues .values ():
224+ # Protect from overflow. It's ok to discard old messages.
225+ if q .full ():
226+ q .get_nowait ()
227+ await q .put (msg_json )
228+
229+ def start (self ):
230+ pass
231+
232+ async def stop (self ):
233+ pass
234+
235+
236+ class SystemInfoStream :
237+ def __init__ (self , * , rm_ref ):
238+ self ._RM = rm_ref
239+ self ._queues_status = {}
240+ self ._queues_info = {}
241+ self ._background_task = None
242+ self ._background_task_running = False
243+ self ._background_task_stopped = asyncio .Event ()
244+ self ._background_task_stopped .set ()
245+ self ._num = 0
246+ self ._queue_max_size = 1000
247+
248+ @property
249+ def background_task_running (self ):
250+ return self ._background_task_running
251+
252+ @property
253+ def queues_status (self ):
254+ return self ._queues_status
255+
256+ @property
257+ def queues_info (self ):
258+ return self ._queues_info
259+
260+ def add_queue_status (self , key ):
261+ """
262+ Add a new queue to the dictionary of queues. The key is a reference to the socket for
263+ for connection with the client.
264+ """
265+ queue = asyncio .Queue (maxsize = self ._queue_max_size )
266+ self ._queues_status [key ] = queue
267+ return queue
268+
269+ def add_queue_info (self , key ):
270+ """
271+ Add a new queue to the dictionary of queues. The key is a reference to the socket for
272+ for connection with the client.
273+ """
274+ queue = asyncio .Queue (maxsize = self ._queue_max_size )
275+ self ._queues_info [key ] = queue
276+ return queue
277+
278+ def remove_queue_status (self , key ):
279+ """
280+ Remove the queue identified by the key from the dictionary of queues.
281+ """
282+ if key in self ._queues_status :
283+ del self ._queues_status [key ]
284+
285+ def remove_queue_info (self , key ):
286+ """
287+ Remove the queue identified by the key from the dictionary of queues.
288+ """
289+ if key in self ._queues_info :
290+ del self ._queues_info [key ]
291+
292+ def _start_background_task (self ):
293+ if not self ._background_task_running :
294+ self ._background_task = asyncio .create_task (self ._load_msgs_task ())
295+
296+ async def _stop_background_task (self ):
297+ self ._background_task_running = False
298+ await self ._background_task_stopped .wait ()
299+
300+ async def _load_msgs_task (self ):
301+ self ._background_task_stopped .clear ()
302+ self ._background_task_running = True
303+ while self ._background_task_running :
304+ try :
305+ msg = await self ._RM .system_info_monitor .next_msg (timeout = 0.5 )
306+
307+ if isinstance (msg , dict ) and "msg" in msg :
308+ msg_json = json .dumps (msg )
309+ # ALL 'info' messages
310+ for q in self ._queues_info .values ():
311+ # Protect from overflow. It's ok to discard old messages.
312+ if q .full ():
313+ q .get_nowait ()
314+ await q .put (msg_json )
315+ if isinstance (msg ["msg" ], dict ) and "status" in msg ["msg" ]:
316+ # ONLY 'status' messages
317+ for q in self ._queues_status .values ():
318+ # Protect from overflow. It's ok to discard old messages.
319+ if q .full ():
320+ q .get_nowait ()
321+ await q .put (msg_json )
322+ except self ._RM .RequestTimeoutError :
323+ pass
324+ self ._background_task_stopped .set ()
325+
326+ def start (self ):
327+ self ._RM .system_info_monitor .enable ()
328+ self ._start_background_task ()
329+
330+ async def stop (self ):
331+ await self ._stop_background_task ()
332+ await self ._RM .system_info_monitor .disable_wait ()
0 commit comments