Skip to content

Commit

Permalink
Merge branch 'websocket'
Browse files Browse the repository at this point in the history
  • Loading branch information
atiderko committed Jul 5, 2024
2 parents e1730a5 + 46ec2ad commit bc03d15
Show file tree
Hide file tree
Showing 153 changed files with 6,756 additions and 7,459 deletions.
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,10 @@ Based on the [FKIE Multimaster](https://github.com/fkie/multimaster_fkie), this

## Install

The communication between the GUI and the Daemon (on each host) is based on [WAMP](https://wamp-proto.org/). It needs a running [WAMP Router](https://wamp-proto.org/implementations.html#routers). We use crossbar.
The communication between the GUI and the Daemon (on each host) is based on WebSockets on port __35430+(ROS_DOMAIN_ID)__, 35685+(NetworkId) with ROS1. These ports should be open in the firewall.

### Install dependencies

The code have been tested with `Crossbar v22.2.1`:

```bash
sudo snap install crossbar
```

You need a running [TTYD](https://github.com/tsl0922/ttyd) to show screen or log output of the nodes.

```bash
Expand All @@ -30,6 +24,8 @@ In Linux, we need `libsecret-1-dev` to safely store SSH credentials.
sudo apt install libsecret-1-dev
```

> In ROS2 we use a discovery node to get host information for each ROS node. Currently the discovery node depends on the __rmw_fastrtps_cpp__ ROS library. If you are using a different DDS for your system, you will need to change the environment variable for __mas-discovery__ by setting ```export RMW_IMPLEMENTATION=rmw_fastrtps_cpp```.
### Build ROS FKIE packages

You can run the following commands to setup a build from source:
Expand Down
6 changes: 6 additions & 0 deletions fkie_mas_daemon/CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
Changelog for package fkie_mas_daemon
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

3.0.0 (2024-07-05)
------------------
* fkie_mas_daemon: replaced crossbar by websocket
* fkie_mas_daemon: changed kill signal to SIGTERM to stop nodes in ROS2
* Contributors: Alexander Tiderko

2.0.0 (2024-01-24)
------------------
* fkie_mas_daemon: new version based on fkie_multimaster
Expand Down
2 changes: 1 addition & 1 deletion fkie_mas_daemon/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MAS Daemon is an instance which allows the MAS Gui a remote access to configuration files . Through the daemon the launch file can be edited, loaded and the containing nodes executed by MAS GUI.

The daemon instance is usually launched be MAS Gui through SSH connection. After that the MAS Gui communicates with daemon using [WAMP](https://wamp-proto.org/). It needs a running [WAMP Router](https://wamp-proto.org/implementations.html#routers). We use crossbar.
The daemon instance is usually launched be MAS Gui through SSH connection. After that the MAS Gui communicates with daemon using WebSockets on port 35430+(ROS_DOMAIN_ID), __35685+(NetworkId) with ROS1__. These ports should be open in the firewall.

Beside offering remote configuration access to MAS Gui the daemon supports many other features, e.g. system monitoring, forwarding diagnostic messages or auto start/load of launchfiles.

Expand Down
30 changes: 8 additions & 22 deletions fkie_mas_daemon/fkie_mas_daemon/__init__.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,10 @@
# The MIT License (MIT)

# Copyright (c) 2014-2024 Fraunhofer FKIE, Alexander Tiderko

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ****************************************************************************
#
# Copyright (c) 2014-2024 Fraunhofer FKIE
# Author: Alexander Tiderko
# License: MIT
#
# ****************************************************************************


from rclpy.node import Node
Expand All @@ -32,7 +18,7 @@
# # package is not installed
# pass

# the rosnode is assigned in :class:RosNodeLauncher while init
# the ros_node is assigned in :class:RosNodeLauncher while init
ros_node: Node = None
launcher: RosNodeLauncher = None

Expand Down
28 changes: 7 additions & 21 deletions fkie_mas_daemon/fkie_mas_daemon/file_item.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,10 @@
# The MIT License (MIT)

# Copyright (c) 2014-2024 Fraunhofer FKIE, Alexander Tiderko

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ****************************************************************************
#
# Copyright (c) 2014-2024 Fraunhofer FKIE
# Author: Alexander Tiderko
# License: MIT
#
# ****************************************************************************


EFILE_CHANGED = 125
Expand Down
82 changes: 33 additions & 49 deletions fkie_mas_daemon/fkie_mas_daemon/file_servicer.py
Original file line number Diff line number Diff line change
@@ -1,66 +1,54 @@
# The MIT License (MIT)

# Copyright (c) 2014-2024 Fraunhofer FKIE, Alexander Tiderko

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ****************************************************************************
#
# Copyright (c) 2014-2024 Fraunhofer FKIE
# Author: Alexander Tiderko
# License: MIT
#
# ****************************************************************************


from io import FileIO
import os
import re

import asyncio
from autobahn import wamp
import json
from types import SimpleNamespace
from typing import List
from fkie_mas_pylib import ros_pkg
from fkie_mas_pylib.crossbar.base_session import CrossbarBaseSession
from fkie_mas_pylib.crossbar.base_session import SelfEncoder
from fkie_mas_pylib.crossbar.file_interface import FileItem
from fkie_mas_pylib.crossbar.file_interface import RosPackage
from fkie_mas_pylib.crossbar.file_interface import PathItem
from fkie_mas_pylib.crossbar.file_interface import LogPathItem
from fkie_mas_pylib.crossbar.file_interface import LogPathClearResult
from fkie_mas_pylib.interface import SelfEncoder
from fkie_mas_pylib.interface.file_interface import FileItem
from fkie_mas_pylib.interface.file_interface import RosPackage
from fkie_mas_pylib.interface.file_interface import PathItem
from fkie_mas_pylib.interface.file_interface import LogPathItem
from fkie_mas_pylib.interface.file_interface import LogPathClearResult
from fkie_mas_pylib.logging.logging import Log
from fkie_mas_pylib.system.screen import get_logfile
from fkie_mas_pylib.system.screen import get_ros_logfile
from fkie_mas_pylib.websocket.server import WebSocketServer


class FileServicer(CrossbarBaseSession):
class FileServicer:
FILE_CHUNK_SIZE = 1024

def __init__(
self, loop: asyncio.AbstractEventLoop, realm: str = "ros", port: int = 11911
):
def __init__(self, websocket: WebSocketServer):
Log.info("Create ROS2 file manager servicer")
CrossbarBaseSession.__init__(self, loop, realm, port)
# TODO: clear cache after detected change or time?
self.CB_DIR_CACHE = {}
websocket.register("ros.packages.get_list", self.getPackageList)
websocket.register("ros.path.get_log_paths", self.getLogPaths)
websocket.register("ros.path.clear_log_paths", self.clearLogPaths)
websocket.register("ros.path.get_list", self.getPathList)
websocket.register("ros.path.get_list_recursive",
self.getPathListRecursive)
websocket.register("ros.file.get", self.getFileContent)
websocket.register("ros.file.save", self.saveFileContent)

def stop(self):
""" """
self.shutdown()
pass

@wamp.register("ros.packages.get_list")
def getPackageList(self, clear_cache: bool = False) -> List[RosPackage]:
Log.info(f"{self.__class__.__name__}: Request to [ros.packages.get_list]")
Log.info(
f"{self.__class__.__name__}: Request to [ros.packages.get_list]")
clear_cache = False
if clear_cache:
try:
Expand All @@ -76,11 +64,11 @@ def getPackageList(self, clear_cache: bool = False) -> List[RosPackage]:
# fill the input fields
ret = ros_pkg.get_packages(None)
for name, path in ret.items():
package = RosPackage(name=name, path=os.path.join(path, "share", name))
package = RosPackage(
name=name, path=os.path.join(path, "share", name))
package_list.append(package)
return json.dumps(package_list, cls=SelfEncoder)

@wamp.register("ros.path.get_log_paths")
def getLogPaths(self, nodes: List[str]) -> List[LogPathItem]:
Log.info(
f"{self.__class__.__name__}: Request to [ros.path.get_log_paths] for {nodes}"
Expand Down Expand Up @@ -109,7 +97,6 @@ def getLogPaths(self, nodes: List[str]) -> List[LogPathItem]:
result.append(log_path_item)
return json.dumps(result, cls=SelfEncoder)

@wamp.register("ros.path.clear_log_paths")
def clearLogPaths(self, nodes: List[str]) -> List[LogPathClearResult]:
Log.info(
f"{self.__class__.__name__}: Request to [ros.path.clear_log_paths] for {nodes}"
Expand Down Expand Up @@ -150,7 +137,6 @@ def clearLogPaths(self, nodes: List[str]) -> List[LogPathClearResult]:
result.append(log_path_item)
return json.dumps(result, cls=SelfEncoder)

@wamp.register("ros.path.get_list")
def getPathList(self, inputPath: str) -> List[PathItem]:
Log.info(
f"{self.__class__.__name__}: Request to [ros.path.get_list] for {inputPath}"
Expand Down Expand Up @@ -236,7 +222,6 @@ def _glob(
)
return path_list

@wamp.register("ros.path.get_list_recursive")
def getPathListRecursive(self, inputPath: str) -> List[PathItem]:
Log.info(
f"{self.__class__.__name__}: Request to [ros.path.get_list_recursive] for {inputPath}"
Expand All @@ -247,7 +232,6 @@ def getPathListRecursive(self, inputPath: str) -> List[PathItem]:

return json.dumps(path_list, cls=SelfEncoder)

@wamp.register("ros.file.get")
def getFileContent(self, requestPath: str) -> FileItem:
Log.info("Request to [ros.file.get] for %s" % requestPath)
with FileIO(requestPath, "r") as outfile:
Expand All @@ -264,12 +248,12 @@ def getFileContent(self, requestPath: str) -> FileItem:
FileItem(requestPath, mTime, fSize, content, encoding), cls=SelfEncoder
)

@wamp.register("ros.file.save")
def saveFileContent(self, request_json: FileItem) -> int:
# Covert input dictionary into a proper python object
file = json.loads(
json.dumps(request_json), object_hook=lambda d: SimpleNamespace(**d)
)
file = request_json
# file = json.loads(
# json.dumps(request_json), object_hook=lambda d: SimpleNamespace(**d)
# )
Log.info("Request to [ros.file.save] for %s" % file.path)
with FileIO(file.path, "w+") as outfile:
content = file.value
Expand Down
43 changes: 15 additions & 28 deletions fkie_mas_daemon/fkie_mas_daemon/launch_config.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,10 @@
# The MIT License (MIT)

# Copyright (c) 2014-2024 Fraunhofer FKIE, Alexander Tiderko

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ****************************************************************************
#
# Copyright (c) 2014-2024 Fraunhofer FKIE
# Author: Alexander Tiderko
# License: MIT
#
# ****************************************************************************


from typing import Dict
Expand Down Expand Up @@ -52,9 +38,9 @@
import launch_ros
import composition_interfaces.srv

from fkie_mas_pylib.crossbar.runtime_interface import RosNode
from fkie_mas_pylib.crossbar.launch_interface import LaunchArgument
from fkie_mas_pylib.crossbar.launch_interface import LaunchNodeInfo
from fkie_mas_pylib.interface.runtime_interface import RosNode
from fkie_mas_pylib.interface.launch_interface import LaunchArgument
from fkie_mas_pylib.interface.launch_interface import LaunchNodeInfo
from fkie_mas_pylib.logging.logging import Log
from fkie_mas_pylib import names
from fkie_mas_pylib import ros_pkg
Expand Down Expand Up @@ -724,13 +710,14 @@ def _replace_arg(self, arg, argv_defaults, argv_values):
arg_match = re.search(r"\$\(\s*arg\s*", value)

@classmethod
def get_launch_arguments(cls, filename: str, provided_args: list) -> List[LaunchArgument]:
def get_launch_arguments(cls, context: LaunchContext, filename: str, provided_args: list) -> List[LaunchArgument]:
'''
:param list(fkie_mas_msgs.crossbar.runtime_interface.RosParameter) provided_args: provided args used to set 'value' in returned args
:param list(fkie_mas_pylib.interface.runtime_interface.RosParameter) provided_args: provided args used to set 'value' in returned args
:return: a list with args being used in the roslaunch file.
:rtype: list(fkie_mas_msgs.crossbar.runtime_interface.RosParameter)
:rtype: list(fkie_mas_pylib.interface.runtime_interface.RosParameter)
'''
context = LaunchContext()

# context = LaunchContext()
launch_description = get_launch_description_from_any_launch_file(
filename)
launch_arguments: List[launch.actions.declare_launch_argument.DeclareLaunchArgument] = launch_description.get_launch_arguments()
Expand Down
Loading

0 comments on commit bc03d15

Please sign in to comment.