Skip to content

Commit 6efa753

Browse files
committed
Refactor of singleton
1 parent 14c73e1 commit 6efa753

1 file changed

Lines changed: 21 additions & 21 deletions

File tree

mirte_robot/robot.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,6 @@
5151

5252
mirte = {}
5353

54-
# No QoS Profiles are set, but this might not be required, since they might already behave like ROS 1 persistant.
55-
56-
def singleton(cls):
57-
instances = {}
58-
59-
def get_instance(*args, **kwargs):
60-
if cls not in instances:
61-
instances[cls] = cls(*args, **kwargs)
62-
return instances[cls]
63-
64-
return get_instance
65-
66-
# TODO: We should not decorate the class here with @singleton. That will
67-
# prevent sphinx autodoc from generating the docs for this class. But the
68-
# previous check did not work.
69-
@singleton
7054
class Robot:
7155
"""Robot API
7256
@@ -76,14 +60,30 @@ class Robot:
7660

7761
# Implementation Notes:
7862
# This class creates a hidden ROS Node for the communication.
79-
# This should only be run once, however this can not be prevented from the web interface.
63+
# This class is a singleton.
64+
# No QoS Profiles are set, but this might not be required, since they might already behave like ROS 1 persistant.
8065
# Therefore the node is also anonymized with the current time.
8166

67+
# Ensuring singleton behavior
68+
# This should not be done as a decorator, since the decorator returns a
69+
# function. Therefor the sphynx documentation is not able to process
70+
# this class anymore.
71+
_instance = None
72+
def __new__(cls, *args, **kwargs):
73+
if cls._instance is None:
74+
cls._instance = super(Robot, cls).__new__(cls)
75+
rclpy.init()
76+
return cls._instance
77+
8278
def __init__(
8379
self, machine_namespace: Optional[str] = None, hardware_namespace: str = "io"
8480
):
8581
"""Intialize the Mirte Robot API"""
8682

83+
# Only the first instance (singelton) needs to be initialized
84+
if getattr(self, "_initialized", False):
85+
return
86+
8787
# Parameters:
8888
# machine_namespace (Optional[str], optional): The Namespace from '/' to the ROS namespace for the specific Mirte. Defaults to "/{HOSTNAME}". (This only has to be changed when running the Robot API from a different machine directly. It is configured correctly for the Web interface)
8989
# hardware_namespace (str, optional): The namespace for the hardware peripherals. Defaults to "io".
@@ -105,10 +105,8 @@ def __init__(
105105

106106
ROS_DISTRO = os.getenv("ROS_DISTRO")
107107

108-
rclpy.init()
109-
110-
# This node should be only ran once.
111-
# No 'anonymous' flag available, so use unix epoch nano seconds to pad name
108+
# In order to be able to run this from multiple python files (ie creating
109+
# multtiple nodes) a timestamp is added (replicating a 'anonymous' node)
112110
self._node = rclpy.node.Node(
113111
"_mirte_python_api_" + str(time.time_ns()),
114112
namespace=self._machine_namespace,
@@ -436,6 +434,8 @@ def finalize(node: rclpy.node.Node, motors: dict[str, rclpy.client.Client]):
436434
signal.signal(signal.SIGINT, self._signal_handler)
437435
signal.signal(signal.SIGTERM, self._signal_handler)
438436

437+
self._initialized = True
438+
439439
def _call_service(
440440
self, client: rclpy.client.Client, request: rclpy.client.SrvTypeRequest
441441
) -> rclpy.client.SrvTypeResponse:

0 commit comments

Comments
 (0)