You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by hu...@apache.org on 2024/05/29 04:10:52 UTC

(plc4x) branch plc4py/transport_plugin created (now 7889c87738)

This is an automated email from the ASF dual-hosted git repository.

hutcheb pushed a change to branch plc4py/transport_plugin
in repository https://gitbox.apache.org/repos/asf/plc4x.git


      at 7889c87738 fix(plc4py): Add plugin interface for transports

This branch includes the following new commits:

     new 7889c87738 fix(plc4py): Add plugin interface for transports

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(plc4x) 01/01: fix(plc4py): Add plugin interface for transports

Posted by hu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hutcheb pushed a commit to branch plc4py/transport_plugin
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 7889c877385034d97d1b4a9addc21e4da8566e08
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Wed May 29 06:10:40 2024 +0200

    fix(plc4py): Add plugin interface for transports
---
 plc4py/plc4py/PlcDriverManager.py                  | 49 +++++++++++++++++++++-
 plc4py/plc4py/__init__.py                          |  4 --
 plc4py/plc4py/drivers/__init__.py                  |  4 ++
 plc4py/plc4py/drivers/mock/MockConnection.py       |  4 +-
 plc4py/plc4py/drivers/modbus/ModbusConnection.py   |  4 +-
 plc4py/plc4py/drivers/umas/UmasConnection.py       |  4 +-
 plc4py/plc4py/spi/PlcDriverClassLoader.py          | 19 ++++++++-
 plc4py/plc4py/spi/generation/WriteBuffer.py        |  2 -
 plc4py/plc4py/spi/transport/MockTransport.py       | 22 +++++++++-
 plc4py/plc4py/spi/transport/PlcTransportLoader.py  | 36 ++++++++++++++++
 plc4py/plc4py/spi/transport/TCPTransport.py        | 20 ++++++++-
 plc4py/plc4py/spi/transport/__init__.py            |  4 ++
 plc4py/setup.py                                    |  6 ++-
 .../drivers/modbus/test_modbus_connection.py       |  2 +-
 14 files changed, 162 insertions(+), 18 deletions(-)

diff --git a/plc4py/plc4py/PlcDriverManager.py b/plc4py/plc4py/PlcDriverManager.py
index 9180c0fd3d..3a3040a0e7 100644
--- a/plc4py/plc4py/PlcDriverManager.py
+++ b/plc4py/plc4py/PlcDriverManager.py
@@ -27,12 +27,22 @@ from plc4py.api.PlcConnection import PlcConnection
 from plc4py.api.PlcDriver import PlcDriver
 from plc4py.spi.PlcDriverClassLoader import PlcDriverClassLoader
 from plc4py.utils.ConnectionStringHandling import get_protocol_code
+from plc4py.spi.PlcDriverClassLoader import PlcTransportClassLoader
+from plc4py.spi.transport.Plc4xBaseTransport import Plc4xBaseTransport
 
 
 @dataclass
 class PlcDriverManager:
-    class_loader: PluginManager = field(default_factory=lambda: PluginManager("plc4py"))
+    class_loader: PluginManager = field(
+        default_factory=lambda: PluginManager("plc4py.drivers")
+    )
+    transport_loader: PluginManager = field(
+        default_factory=lambda: PluginManager("plc4py.transports")
+    )
     _driver_map: Dict[str, Type[PlcDriver]] = field(default_factory=lambda: {})
+    _transport_map: Dict[str, Type[Plc4xBaseTransport]] = field(
+        default_factory=lambda: {}
+    )
 
     def __post_init__(self):
         """
@@ -79,6 +89,43 @@ class PlcDriverManager:
         # Check for any pending plugins
         self.class_loader.check_pending()
 
+        ######
+        # Log the class loader used
+        logging.info(
+            "Instantiating new PLC Transport Manager with class loader %s",
+            self.transport_loader,
+        )
+
+        # Add the PlcDriverClassLoader hookspecs to the class loader
+        self.transport_loader.add_hookspecs(PlcTransportClassLoader)
+
+        # Log the registration of drivers
+        logging.info("Registering available transports...")
+
+        # Register the plc4py.drivers package
+        import plc4py.spi.transport
+
+        self.transport_loader.register(plc4py.spi.transport)
+
+        # Load the setuptools entry points defined in the "plc4py.drivers" namespace
+        self.transport_loader.load_setuptools_entrypoints("plc4py.transports")
+
+        # Create a dictionary mapping the hook names to the PlcDriver instances
+        self._transport_map = {
+            key: loader
+            for key, loader in zip(
+                self.transport_loader.hook.key(),
+                self.transport_loader.hook.get_transport(),
+            )
+        }
+
+        # Log the successful registration of each driver
+        for transport in self._transport_map:
+            logging.info("... %s .. OK", transport)
+
+        # Check for any pending plugins
+        self.transport_loader.check_pending()
+
     @asynccontextmanager
     async def connection(self, url: str) -> AsyncIterator[PlcConnection]:
         """
diff --git a/plc4py/plc4py/__init__.py b/plc4py/plc4py/__init__.py
index edb4c5ab16..22eb734c9c 100644
--- a/plc4py/plc4py/__init__.py
+++ b/plc4py/plc4py/__init__.py
@@ -21,9 +21,5 @@ __version__ = "0.1.0"
 
 import logging
 
-import pluggy  # type: ignore
-
 # Place holder for logging configuration
 logging.basicConfig(level=logging.DEBUG)
-
-hookimpl = pluggy.HookimplMarker("plc4py")
diff --git a/plc4py/plc4py/drivers/__init__.py b/plc4py/plc4py/drivers/__init__.py
index 342be71799..e9877cb638 100644
--- a/plc4py/plc4py/drivers/__init__.py
+++ b/plc4py/plc4py/drivers/__init__.py
@@ -16,3 +16,7 @@
 # specific language governing permissions and limitations
 # under the License.
 #
+
+import pluggy  # type: ignore
+
+hookimpl = pluggy.HookimplMarker("plc4py.drivers")
diff --git a/plc4py/plc4py/drivers/mock/MockConnection.py b/plc4py/plc4py/drivers/mock/MockConnection.py
index 77a00437b8..ad5b5292ad 100644
--- a/plc4py/plc4py/drivers/mock/MockConnection.py
+++ b/plc4py/plc4py/drivers/mock/MockConnection.py
@@ -137,11 +137,11 @@ class MockDriverLoader(PlcDriverLoader):
     """
 
     @staticmethod
-    @plc4py.hookimpl
+    @plc4py.drivers.hookimpl
     def get_driver() -> Type[MockDriver]:
         return MockDriver
 
     @staticmethod
-    @plc4py.hookimpl
+    @plc4py.drivers.hookimpl
     def key() -> str:
         return "mock"
diff --git a/plc4py/plc4py/drivers/modbus/ModbusConnection.py b/plc4py/plc4py/drivers/modbus/ModbusConnection.py
index 0a2a01baeb..cb7e2dd8bd 100644
--- a/plc4py/plc4py/drivers/modbus/ModbusConnection.py
+++ b/plc4py/plc4py/drivers/modbus/ModbusConnection.py
@@ -175,7 +175,7 @@ class ModbusDriverLoader(PlcDriverLoader):
     """
 
     @staticmethod
-    @plc4py.hookimpl
+    @plc4py.drivers.hookimpl
     def get_driver() -> Type[ModbusDriver]:
         """
         Returns the ModbusDriver class used to instantiate Modbus plc drivers.
@@ -187,7 +187,7 @@ class ModbusDriverLoader(PlcDriverLoader):
         return ModbusDriver
 
     @staticmethod
-    @plc4py.hookimpl
+    @plc4py.drivers.hookimpl
     def key() -> str:
         """
         Returns the unique key for the Modbus driver.
diff --git a/plc4py/plc4py/drivers/umas/UmasConnection.py b/plc4py/plc4py/drivers/umas/UmasConnection.py
index 5183151011..3f9112d497 100644
--- a/plc4py/plc4py/drivers/umas/UmasConnection.py
+++ b/plc4py/plc4py/drivers/umas/UmasConnection.py
@@ -162,11 +162,11 @@ class UmasDriverLoader(PlcDriverLoader):
     """
 
     @staticmethod
-    @plc4py.hookimpl
+    @plc4py.drivers.hookimpl
     def get_driver() -> Type[UmasDriver]:
         return UmasDriver
 
     @staticmethod
-    @plc4py.hookimpl
+    @plc4py.drivers.hookimpl
     def key() -> str:
         return "umas"
diff --git a/plc4py/plc4py/spi/PlcDriverClassLoader.py b/plc4py/plc4py/spi/PlcDriverClassLoader.py
index 0bb58f78c2..b676b7086b 100644
--- a/plc4py/plc4py/spi/PlcDriverClassLoader.py
+++ b/plc4py/plc4py/spi/PlcDriverClassLoader.py
@@ -21,12 +21,13 @@ from typing import Type
 import pluggy
 
 from plc4py.api.PlcDriver import PlcDriver
+from plc4py.spi.transport.Plc4xBaseTransport import Plc4xBaseTransport
 
 
 class PlcDriverClassLoader:
     """Hook spec for PLC4PY Driver Loaders"""
 
-    hookspec = pluggy.HookspecMarker("plc4py")
+    hookspec = pluggy.HookspecMarker("plc4py.drivers")
 
     @staticmethod
     @hookspec
@@ -37,3 +38,19 @@ class PlcDriverClassLoader:
     @hookspec
     def key() -> str:
         """Unique key to identify the driver"""
+
+
+class PlcTransportClassLoader:
+    """Hook spec for PLC4PY Transport Loaders"""
+
+    hookspec = pluggy.HookspecMarker("plc4py.transports")
+
+    @staticmethod
+    @hookspec
+    def get_transport() -> Type[Plc4xBaseTransport]:
+        """Returns the PlcConnection class that is used to instantiate the transport"""
+
+    @staticmethod
+    @hookspec
+    def key() -> str:
+        """Unique key to identify the transport"""
diff --git a/plc4py/plc4py/spi/generation/WriteBuffer.py b/plc4py/plc4py/spi/generation/WriteBuffer.py
index 348ddc8584..d611dbd391 100644
--- a/plc4py/plc4py/spi/generation/WriteBuffer.py
+++ b/plc4py/plc4py/spi/generation/WriteBuffer.py
@@ -36,8 +36,6 @@ from typing import List, Union
 
 from bitarray import bitarray
 from bitarray.util import zeros
-from typing_extensions import override
-
 from plc4py.api.exceptions.exceptions import SerializationException
 from plc4py.api.messages.PlcMessage import PlcMessage
 from plc4py.utils.GenericTypes import ByteOrder, ByteOrderAware
diff --git a/plc4py/plc4py/spi/transport/MockTransport.py b/plc4py/plc4py/spi/transport/MockTransport.py
index e0a740e866..05c4c78c55 100644
--- a/plc4py/plc4py/spi/transport/MockTransport.py
+++ b/plc4py/plc4py/spi/transport/MockTransport.py
@@ -20,9 +20,13 @@ import asyncio
 import logging
 from asyncio import Protocol
 from dataclasses import dataclass
-from typing import Callable
+from typing import Callable, Type
 
 from plc4py.spi.transport.Plc4xBaseTransport import Plc4xBaseTransport
+import plc4py
+from plc4py.spi.transport.PlcTransportLoader import PlcTransportLoader
+
+from plc4py.spi.transport.TCPTransport import TCPTransport
 
 
 @dataclass
@@ -45,3 +49,19 @@ class MockTransport(Plc4xBaseTransport):
         protocol_factory: Callable[[], Protocol], host: str, port: int
     ) -> Plc4xBaseTransport:
         return MockTransport(None, None, host, port)
+
+
+class MockTransportLoader(PlcTransportLoader):
+    """
+    Umas Driver Pluggy Hook Implementation, lets pluggy find the driver by name
+    """
+
+    @staticmethod
+    @plc4py.spi.transport.hookimpl
+    def get_transport() -> Type[MockTransport]:
+        return MockTransport
+
+    @staticmethod
+    @plc4py.spi.transport.hookimpl
+    def key() -> str:
+        return "mock"
diff --git a/plc4py/plc4py/spi/transport/PlcTransportLoader.py b/plc4py/plc4py/spi/transport/PlcTransportLoader.py
new file mode 100644
index 0000000000..2149b492ef
--- /dev/null
+++ b/plc4py/plc4py/spi/transport/PlcTransportLoader.py
@@ -0,0 +1,36 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+from abc import abstractmethod
+from typing import Type
+
+from plc4py.spi.transport.Plc4xBaseTransport import Plc4xBaseTransport
+
+
+class PlcTransportLoader:
+    """Hook spec for PLC4PY Transport Loaders"""
+
+    @staticmethod
+    @abstractmethod
+    def get_transport() -> Type[Plc4xBaseTransport]:
+        """Returns the PlcConnection class that is used to instantiate the transport"""
+        pass
+
+    @staticmethod
+    @abstractmethod
+    def key() -> str:
+        """Unique key to identify the transport"""
+        pass
diff --git a/plc4py/plc4py/spi/transport/TCPTransport.py b/plc4py/plc4py/spi/transport/TCPTransport.py
index 62422ec213..fbb275098b 100644
--- a/plc4py/plc4py/spi/transport/TCPTransport.py
+++ b/plc4py/plc4py/spi/transport/TCPTransport.py
@@ -19,9 +19,11 @@
 import asyncio
 from asyncio import Protocol
 from dataclasses import dataclass
-from typing import Callable
+from typing import Callable, Type
 
+import plc4py
 from plc4py.spi.transport.Plc4xBaseTransport import Plc4xBaseTransport
+from plc4py.spi.transport.PlcTransportLoader import PlcTransportLoader
 
 
 @dataclass
@@ -44,3 +46,19 @@ class TCPTransport(Plc4xBaseTransport):
         coro = loop.create_connection(protocol_factory, host, port)
         _transport, _protocol = await coro
         return TCPTransport(_transport, _protocol, host, port)
+
+
+class TCPTransportLoader(PlcTransportLoader):
+    """
+    Umas Driver Pluggy Hook Implementation, lets pluggy find the driver by name
+    """
+
+    @staticmethod
+    @plc4py.spi.transport.hookimpl
+    def get_transport() -> Type[TCPTransport]:
+        return TCPTransport
+
+    @staticmethod
+    @plc4py.spi.transport.hookimpl
+    def key() -> str:
+        return "tcp"
diff --git a/plc4py/plc4py/spi/transport/__init__.py b/plc4py/plc4py/spi/transport/__init__.py
index 342be71799..6919c7a9c1 100644
--- a/plc4py/plc4py/spi/transport/__init__.py
+++ b/plc4py/plc4py/spi/transport/__init__.py
@@ -16,3 +16,7 @@
 # specific language governing permissions and limitations
 # under the License.
 #
+
+import pluggy  # type: ignore
+
+hookimpl = pluggy.HookimplMarker("plc4py.transports")
diff --git a/plc4py/setup.py b/plc4py/setup.py
index 82bf257669..2566c3ccfd 100644
--- a/plc4py/setup.py
+++ b/plc4py/setup.py
@@ -63,6 +63,10 @@ setup(
             "mock = plc4py.drivers.mock.MockConnection:MockDriverLoader",
             "modbus = plc4py.drivers.modbus.ModbusConnection:ModbusDriverLoader",
             "umas = plc4py.drivers.umas.UmasConnection:UmasDriverLoader",
-        ]
+        ],
+        "plc4py.transports": [
+            "tcp = plc4py.spi.transport.TCPTransport:TCPTransportLoader",
+            "mock = plc4py.spi.transport.MockTransport:MockTransportLoader",
+        ],
     },
 )
diff --git a/plc4py/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py b/plc4py/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py
index 328f699a77..1fd9c49e6c 100644
--- a/plc4py/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py
+++ b/plc4py/tests/unit/plc4py/drivers/modbus/test_modbus_connection.py
@@ -67,6 +67,6 @@ async def test_plc_driver_modbus_read():
 
             response = await future
             value = response.tags["Random Tag"].value
-            log.error("Read tag 4x00001[10] - %s", value)
+            log.info("Read tag 4x00001[10] - %s", value)
 
     pass