Просмотр исходного кода

dnet/script: cleanup, create model and subscribe()

lunar-mining 2 лет назад
Родитель
Сommit
020b3899cc
5 измененных файлов с 192 добавлено и 105 удалено
  1. 5 4
      script/dnet/config.toml
  2. 71 74
      script/dnet/main.py
  3. 78 0
      script/dnet/model.py
  4. 2 5
      script/dnet/rpc.py
  5. 36 22
      script/dnet/view.py

+ 5 - 4
script/dnet/config.toml

@@ -1,7 +1,8 @@
-#[[nodes]]
-#name = "ircd"
-#port = 10555
-
 [[nodes]]
 name = "darkirc"
 port = 26660
+
+[[nodes]]
+name = "taud"
+port = 23330
+

+ 71 - 74
script/dnet/main.py

@@ -15,81 +15,78 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-import sys, toml, urwid, asyncio, logging
+import sys, toml, json, urwid, asyncio, logging
 
-import model
+from model import Model
 from rpc import JsonRpc
-from view import Dnetview
-
-async def get_info(rpc, name, port):
-    while True:
-        try:
-            await rpc.start("localhost", port)
-            break
-        except OSError:
-            pass
-    response = await rpc._make_request("p2p.get_info", [])
-    info = response["result"]
-    channels = info["channels"]
-    channel_lookup = {}
-    for channel in channels:
-        id = channel["id"]
-        channel_lookup[id] = channel
-
-    logging.debug(f"{name}")
-    logging.debug("inbound")
-    for channel in channels:
-        if channel["session"] != "inbound":
-            continue
-        url = channel["url"]
-        logging.debug(f"  {url}")
-
-    logging.debug("outbound")
-    for i, id in enumerate(info["outbound_slots"]):
-        if id == 0:
-            logging.debug(f"  {i}: none")
-            continue
-
-        assert id in channel_lookup
-        url = channel_lookup[id]["url"]
-        logging.debug(f"  {i}: {url}")
-
-    logging.debug("seed")
-    for channel in channels:
-        if channel["session"] != "seed":
-            continue
-        url = channel["url"]
-        logging.debug(f"  {i}: {url}")
-
-    logging.debug("manual")
-    for channel in channels:
-        if channel["session"] != "manual":
-            continue
-        url = channel["url"]
-        logging.debug(f"  {i}: {url}")
-
-    await rpc.stop()
-
-
-def get_config():
-    with open("config.toml") as f:
-        cfg = toml.load(f)
-        return cfg
-
+from view import View
+
+class Dnetview:
+    def __init__(self):
+        self.ev = asyncio.get_event_loop()
+        self.queue = asyncio.Queue()
+
+        self.config = self.get_config()
+        self.model = Model()
+        self.view = View(self.model)
+
+    async def subscribe(self, rpc, name, port):
+        info = {}
+    
+        while True:
+            try:
+                logging.debug(f"Start {name} RPC on port {port}")
+                await rpc.start("localhost", port)
+                break
+            # TODO: offline node handling
+            except OSError:
+                pass
+    
+        data = await rpc._make_request("p2p.get_info", [])
+        logging.debug(f"get_info: {data}")
+
+        await rpc.dnet_switch(True)
+        await rpc.dnet_subscribe_events()
+
+        while True:
+            data = await rpc.reader.readline()
+            data = json.loads(data)
+            # TODO: update data structures
+            logging.debug(f"events: {data}")
+    
+        await rpc.dnet_switch(False)
+        await rpc.stop()
+    
+    def get_config(self):
+        with open("config.toml") as f:
+            cfg = toml.load(f)
+            return cfg
+    
+    async def start_connect_slots(self, nodes):
+        tasks = []
+        async with asyncio.TaskGroup() as tg:
+            for i, node in enumerate(nodes):
+                rpc = JsonRpc()
+                task = tg.create_task(self.subscribe(rpc, node['name'], node['port']))
+
+    def main(self):
+        logging.basicConfig(filename='dnet.log', encoding='utf-8', level=logging.DEBUG)
+        nodes = self.config.get("nodes")
+
+        self.ev.create_task(self.start_connect_slots(nodes))
+        self.ev.create_task(self.view.update_view(self.model))
+
+        loop = urwid.MainLoop(self.view.ui, self.view.palette,
+            unhandled_input=self.unhandled_input,
+            event_loop=urwid.AsyncioEventLoop(loop=self.ev))
+        loop.run()
+
+    def unhandled_input(self, key):
+        if key in ('q'):
+            for task in asyncio.all_tasks():
+                task.cancel()
+            raise urwid.ExitMainLoop()
+    
 if __name__ == '__main__':
-    logging.basicConfig(filename='dnet.log', encoding='utf-8', level=logging.DEBUG)
-
-    config = get_config()
-    nodes = config.get("nodes")
-
-    ev = asyncio.get_event_loop()
-    rpc = JsonRpc()
-    for node in nodes:
-        ev.create_task(get_info(rpc, node['name'], node['port']))
-
     dnet = Dnetview()
-    ev.create_task(dnet.render_info())
-
-    loop = urwid.MainLoop(dnet.view, dnet.palette,
-        event_loop=urwid.AsyncioEventLoop(loop=ev))
-    loop.run()
+    dnet.main()

+ 78 - 0
script/dnet/model.py

@@ -0,0 +1,78 @@
+# This file is part of DarkFi (https://dark.fi)
+#
+# Copyright (C) 2020-2023 Dyne.org foundation
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+import logging
+
+class Model:
+    def __init__(self):
+        self.nodes = {}
+
+    def update(self, new_node):
+        self.nodes.update(new_node)
+
+    def __repr__(self):
+        return f"{self.nodes}"
+
+class NodeInfo():
+    def __init__(self, channels, slots):
+        self.node = {}
+        inbound = {}
+        outbounds = {"slots": []}
+        manual = {}
+        seed = {}
+
+        for name, channels in info.items():
+            channel_lookup = {}
+            for channel in channels:
+                id = channel["id"]
+                channel_lookup[id] = channel
+
+            for channel in channels:
+                if channel["session"] != "inbound":
+                    continue
+                url = channel["url"]
+                inbound["inbound"] = url
+
+            
+            for i, id in enumerate(slots):
+                if id == 0:
+                    outbounds["slots"].append(f"{i}: none")
+                    continue
+
+                assert id in channel_lookup
+                url = channel_lookup[id]["url"]
+                outbounds["slots"].append(f"{i}: {url}")
+
+            for channel in channels:
+                if channel["session"] != "seed":
+                    continue
+                url = channel["url"]
+                seed["seed"] = url
+
+            for channel in channels:
+                if channel["session"] != "manual":
+                    continue
+                url = channel["url"]
+                manual["manual"] = url
+
+        self.node[name] = [inbound, outbounds, manual,
+                                seed]
+
+    def __repr__(self):
+        return f"{self.node}"
+
+

+ 2 - 5
script/dnet/rpc.py

@@ -15,7 +15,7 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-import asyncio, json, random, time
+import asyncio, json, random, time, logging
 
 class JsonRpc:
     async def start(self, server, port):
@@ -29,7 +29,6 @@ class JsonRpc:
 
     async def _make_request(self, method, params):
         ident = random.randint(0, 2**16)
-        #print(ident)
         request = {
             "jsonrpc": "2.0",
             "method": method,
@@ -40,11 +39,9 @@ class JsonRpc:
         message = json.dumps(request) + "\n"
         self.writer.write(message.encode())
         await self.writer.drain()
-
         data = await self.reader.readline()
         message = data.decode().strip()
         response = json.loads(message)
-        #print(response)
         return response
 
     async def _subscribe(self, method, params):
@@ -59,7 +56,7 @@ class JsonRpc:
         message = json.dumps(request) + "\n"
         self.writer.write(message.encode())
         await self.writer.drain()
-        #print("Subscribed")
+        logging.debug("Subscribed")
 
     async def ping(self):
         return await self._make_request("ping", [])

+ 36 - 22
script/dnet/view.py

@@ -16,9 +16,11 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 import urwid
+import logging
 import asyncio
 
 from scroll import ScrollBar, Scrollable
+from model import NodeInfo
 
 event_loop = asyncio.get_event_loop()
 
@@ -38,8 +40,8 @@ class LeftList(urwid.ListBox):
         return InfoWidget(self)
 
 class ServiceView(urwid.WidgetWrap):
-    def __init__(self):
-        test = urwid.Text("1")
+    def __init__(self, info):
+        test = urwid.Text(f"{info}")
         super().__init__(test)
         self._w = urwid.AttrWrap(self._w, None)
         self.update_w()
@@ -48,8 +50,8 @@ class ServiceView(urwid.WidgetWrap):
         return True
 
     def keypress(self, size, key):
-        if key in ('q'):
-            raise urwid.ExitMainLoop()
+        #if key in ('q'):
+        #    raise urwid.ExitMainLoop()
         return key
 
     def update_w(self):
@@ -59,8 +61,8 @@ class ServiceView(urwid.WidgetWrap):
         return "ServiceView"
 
 class SessionView(urwid.WidgetWrap):
-    def __init__(self):
-        test = urwid.Text("2")
+    def __init__(self, info):
+        test = urwid.Text(f"{info}")
         super().__init__(test)
         self._w = urwid.AttrWrap(self._w, None)
         self.update_w()
@@ -69,8 +71,8 @@ class SessionView(urwid.WidgetWrap):
         return True
 
     def keypress(self, size, key):
-        if key in ('q'):
-            raise urwid.ExitMainLoop()
+        #if key in ('q'):
+        #    raise urwid.ExitMainLoop()
         return key
 
     def update_w(self):
@@ -80,8 +82,8 @@ class SessionView(urwid.WidgetWrap):
         return "SessionView"
 
 class ConnectView(urwid.WidgetWrap):
-    def __init__(self):
-        test = urwid.Text("3")
+    def __init__(self, info):
+        test = urwid.Text(f"{info}")
         super().__init__(test)
         self._w = urwid.AttrWrap(self._w, None)
         self.update_w()
@@ -90,8 +92,8 @@ class ConnectView(urwid.WidgetWrap):
         return True
 
     def keypress(self, size, key):
-        if key in ('q'):
-            raise urwid.ExitMainLoop()
+        #if key in ('q'):
+        #    raise urwid.ExitMainLoop()
         return key
 
     def update_w(self):
@@ -100,37 +102,49 @@ class ConnectView(urwid.WidgetWrap):
     def name(self):
         return "ConnectView"
 
-class Dnetview():
+class View():
     palette = [
               ('body','light gray','black', 'standout'),
               ("line","dark cyan","black","standout"),
               ]
 
-    def __init__(self, data=None):
+    def __init__(self, data=NodeInfo):
+        #logging.debug(f"dnetview init {data}")
+
         info_text = urwid.Text("")
         self.pile = urwid.Pile([info_text])
         scroll = ScrollBar(Scrollable(self.pile))
         rightbox = urwid.LineBox(scroll)
         
-        widget = ServiceView()
-        widget2 = SessionView()
-        widget3 = ConnectView()
+        self.service_info = urwid.Text("")
+        widget = ServiceView(self.service_info)
+
+        self.session_info = urwid.Text("")
+        widget2 = SessionView(self.session_info)
+
+        self.connect_info = urwid.Text("")
+        widget3 = ConnectView(self.connect_info)
 
-        listbox_content = [widget, widget2, widget3]
-        self.listbox = LeftList(urwid.SimpleListWalker(listbox_content))
+        self.listbox_content = [widget, widget2, widget3]
+        self.listbox = LeftList(urwid.SimpleListWalker(self.listbox_content))
         leftbox = urwid.LineBox(self.listbox)
 
         columns = urwid.Columns([leftbox, rightbox], focus_column=0)
-        self.view = urwid.Frame(urwid.AttrWrap( columns, 'body' ))
+        self.ui = urwid.Frame(urwid.AttrWrap( columns, 'body' ))
 
-    async def render_info(self):
+    async def update_view(self, data=NodeInfo):
+        while True:
+            await asyncio.sleep(0.1)
+            self.service_info = urwid.Text("")
+       
+    async def render_info(self, channels):
         while True:
             await asyncio.sleep(0.1)
             self.pile.contents.clear()
             focus_w = self.listbox.get_focus()
             match focus_w[0].name():
                 case "ServiceView":
-                    self.pile.contents.append((urwid.Text("1"), self.pile.options()))
+                    self.pile.contents.append((urwid.Text(f""), self.pile.options()))
                 case "SessionView":
                     self.pile.contents.append((urwid.Text("2"), self.pile.options()))
                 case "ConnectView":