diff --git a/capella_ros_tools/modules/capella/parser.py b/capella_ros_tools/modules/capella/parser.py index da98504..0091d8c 100644 --- a/capella_ros_tools/modules/capella/parser.py +++ b/capella_ros_tools/modules/capella/parser.py @@ -49,18 +49,27 @@ def get_enums(self, package: t.Any) -> list[EnumDef]: """Get enums in Capella model.""" enums = [] for enum in package.enumerations: + values = [] + for literal in enum.owned_literals: + try: + type_name = literal.value.type.name + literal_value = literal.value.value + except AttributeError: + type_name = "" + literal_value = "" + + values.append( + EnumValue( + type_name, + literal.name, + literal_value, + literal.description, + ) + ) enums.append( EnumDef( enum.name, - [ - EnumValue( - literal.value.type.name, - literal.name, - literal.value.value, - literal.description, - ) - for literal in enum.owned_literals - ], + values, enum.description, ) ) diff --git a/capella_ros_tools/scripts/capella2msg.py b/capella_ros_tools/scripts/capella2msg.py index 1655b87..5da434e 100644 --- a/capella_ros_tools/scripts/capella2msg.py +++ b/capella_ros_tools/scripts/capella2msg.py @@ -91,13 +91,14 @@ def _add_package(self, current_root: t.Any) -> MessagePkgDef: [ ConstantDef( BaseTypeDef( - CAPELLA_TYPE_TO_MSG[value.type] + CAPELLA_TYPE_TO_MSG.get(value.type) + or "uint8" ), value.name, - str(value.value), + value.value or str(i), value.description.split("\n"), ) - for value in enum.values + for i, value in enumerate(enum.values) ], [], ) diff --git a/docs/source/examples/Export capella.ipynb b/docs/source/examples/Export capella.ipynb index 8a5e4bb..7341e3f 100644 --- a/docs/source/examples/Export capella.ipynb +++ b/docs/source/examples/Export capella.ipynb @@ -9,29 +9,28 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ - "import capellambse\n", "from capella_ros_tools.scripts import capella2msg\n", "from pathlib import Path" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ - "msg_path = Path(\"data/coffee_msgs\")\n", - "capella_path = capellambse.filehandler.get_filehandler(\"git+https://github.com/DSD-DBS/coffee-machine\")\n", + "msg_path = Path(\"data/test\")\n", + "capella_path = Path(\"data/melody_model_60\")\n", "layer = \"la\"" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -49,7 +48,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ diff --git a/docs/source/examples/Parse capella.ipynb b/docs/source/examples/Parse capella.ipynb new file mode 100644 index 0000000..4fc88f0 --- /dev/null +++ b/docs/source/examples/Parse capella.ipynb @@ -0,0 +1,122 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Parse Capella" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from capella_ros_tools.modules.capella.parser import CapellaModel\n", + "from pathlib import Path" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Cannot load PVMT extension: ValueError: Provided model does not have a PropertyValuePkg\n", + "Property values are not available in this model\n" + ] + } + ], + "source": [ + "model = CapellaModel(Path(\"data/melody_model_60\"), \"la\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'Wand Objects'}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.get_packages(model.data)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[ClassDef(name='Wand', properties=[ClassProperty(type_name='String', type_pkg_name=None, name='owner', min_card='1', max_card='1', description=''), ClassProperty(type_name='Wand Core', type_pkg_name=None, name='core', min_card='1', max_card='1', description=''), ClassProperty(type_name='Wand Wood', type_pkg_name=None, name='wood', min_card='1', max_card='1', description='')], description=''),\n", + " ClassDef(name='Class 2', properties=[], description='')]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.get_classes(model.data)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[EnumDef(name='Wand Core', values=[EnumValue(type='', name='Unicorn Hair', value='', description=''), EnumValue(type='', name='Dragon Heartstring', value='', description=''), EnumValue(type='', name='Pheonix Feather', value='', description=''), EnumValue(type='', name='Thestral Tail-Hair', value='', description='')], description=''),\n", + " EnumDef(name='Wand Wood', values=[EnumValue(type='', name='Apple', value='', description=''), EnumValue(type='', name='Cedar', value='', description=''), EnumValue(type='', name='Ebony', value='', description=''), EnumValue(type='', name='Elder', value='', description=''), EnumValue(type='', name='Holly', value='', description='')], description='')]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.get_enums(model.data)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".capella_venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/source/examples/Parse capella.ipynb.license b/docs/source/examples/Parse capella.ipynb.license new file mode 100644 index 0000000..f6f3181 --- /dev/null +++ b/docs/source/examples/Parse capella.ipynb.license @@ -0,0 +1,2 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: CC0-1.0 diff --git a/docs/source/examples/Parse messages.ipynb b/docs/source/examples/Parse messages.ipynb index 5ce665e..bec90e7 100644 --- a/docs/source/examples/Parse messages.ipynb +++ b/docs/source/examples/Parse messages.ipynb @@ -15,7 +15,7 @@ "source": [ "from capella_ros_tools.modules.messages.parser import MessageDef\n", "from pathlib import Path\n", - "from IPython.display import display\n" + "from IPython.display import display" ] }, { @@ -72,56 +72,10 @@ { "data": { "text/html": [ - "

CameraInfo

# This message defines meta information for a camera. It should be in a
# camera namespace on topic \"camera_info\" and accompanied by up to five
# image topics named:
#
# image_raw - raw data from the camera driver, possibly Bayer encoded
# image - monochrome, distorted
# image_color - color, distorted
# image_rect - monochrome, rectified
# image_rect_color - color, rectified
#
# The image_pipeline contains packages (image_proc, stereo_image_proc)
# for producing the four processed image topics from image_raw and
# camera_info. The meaning of the camera parameters are described in
# detail at http://www.ros.org/wiki/image_pipeline/CameraInfo.
#
# The image_geometry package provides a user-friendly interface to
# common operations using this meta information. If you want to, e.g.,
# project a 3d point into image coordinates, we strongly recommend
# using image_geometry.
#
# If the camera is uncalibrated, the matrices D, K, R, P should be left
# zeroed out. In particular, clients may assume that K[0] == 0.0
# indicates an uncalibrated camera.
#######################################################################
# Image acquisition info #
#######################################################################
#######################################################################
# Calibration Parameters #
#######################################################################
# These are fixed during camera calibration. Their values will be the #
# same in all messages until the camera is recalibrated. Note that #
# self-calibrating systems may \"recalibrate\" frequently. #
# #
# The internal parameters can be used to warp a raw (distorted) image #
# to: #
# 1. An undistorted image (requires D and K) #
# 2. A rectified image (requires D, K, R) #
# The projection matrix P projects 3D points into the rectified image.#
#######################################################################
#######################################################################
# Operational Parameters #
#######################################################################
# These define the image region actually captured by the camera #
# driver. Although they affect the geometry of the output image, they #
# may be changed freely without recalibrating the camera. #
#######################################################################

Fields

  1. std_msgs/Header header
    # Time of image acquisition, camera coordinate frame ID
    # Header timestamp should be acquisition time of image
    # Header frame_id should be optical frame of camera
    # origin of frame should be optical center of camera
    # +x should point to the right in the image
    # +y should point down in the image
    # +z should point into the plane of the image
  2. uint32 height
    # The image dimensions with which the camera was calibrated.
    # Normally this will be the full camera resolution in pixels.
  3. uint32 width
    # The image dimensions with which the camera was calibrated.
    # Normally this will be the full camera resolution in pixels.
  4. string distortion_model
    # The distortion model used. Supported models are listed in
    # sensor_msgs/distortion_models.hpp. For most cameras, \"plumb_bob\" - a
    # simple model of radial and tangential distortion - is sufficent.
  5. float64[] d
    # The distortion parameters, size depending on the distortion model.
    # For \"plumb_bob\", the 5 parameters are: (k1, k2, t1, t2, k3).
  6. float64[9] k
    # Intrinsic camera matrix for the raw (distorted) images.
    # [fx 0 cx]
    # K = [ 0 fy cy]
    # [ 0 0 1]
    # Projects 3D points in the camera coordinate frame to 2D pixel
    # coordinates using the focal lengths (fx, fy) and principal point
    # (cx, cy).
    # 3x3 row-major matrix
  7. float64[9] r
    # Rectification matrix (stereo cameras only)
    # A rotation matrix aligning the camera coordinate system to the ideal
    # stereo image plane so that epipolar lines in both stereo images are
    # parallel.
    # 3x3 row-major matrix
  8. float64[12] p
    # Projection/camera matrix
    # [fx' 0 cx' Tx]
    # P = [ 0 fy' cy' Ty]
    # [ 0 0 1 0]
    # By convention, this matrix specifies the intrinsic (camera) matrix
    # of the processed (rectified) image. That is, the left 3x3 portion
    # is the normal camera intrinsic matrix for the rectified image.
    # It projects 3D points in the camera coordinate frame to 2D pixel
    # coordinates using the focal lengths (fx', fy') and principal point
    # (cx', cy') - these may differ from the values in K.
    # For monocular cameras, Tx = Ty = 0. Normally, monocular cameras will
    # also have R = the identity and P[1:3,1:3] = K.
    # For a stereo pair, the fourth column [Tx Ty 0]' is related to the
    # position of the optical center of the second camera in the first
    # camera's frame. We assume Tz = 0 so both cameras are in the same
    # stereo image plane. The first camera always has Tx = Ty = 0. For
    # the right (second) camera of a horizontal stereo pair, Ty = 0 and
    # Tx = -fx' * B, where B is the baseline between the cameras.
    # Given a 3D point [X Y Z]', the projection (x, y) of the point onto
    # the rectified image is given by:
    # [u v w]' = P * [X Y Z 1]'
    # x = u / w
    # y = v / w
    # This holds for both images of a stereo pair.
    # 3x4 row-major matrix
  9. uint32 binning_x
    # Binning refers here to any camera setting which combines rectangular
    # neighborhoods of pixels into larger \"super-pixels.\" It reduces the
    # resolution of the output image to
    # (width / binning_x) x (height / binning_y).
    # The default values binning_x = binning_y = 0 is considered the same
    # as binning_x = binning_y = 1 (no subsampling).
  10. uint32 binning_y
    # Binning refers here to any camera setting which combines rectangular
    # neighborhoods of pixels into larger \"super-pixels.\" It reduces the
    # resolution of the output image to
    # (width / binning_x) x (height / binning_y).
    # The default values binning_x = binning_y = 0 is considered the same
    # as binning_x = binning_y = 1 (no subsampling).
  11. RegionOfInterest roi
    # Region of interest (subwindow of full camera resolution), given in
    # full resolution (unbinned) image coordinates. A particular ROI
    # always denotes the same window of pixels on the camera sensor,
    # regardless of binning settings.
    # The default setting of roi (all values 0) is considered the same as
    # full resolution (roi.width = width, roi.height = height).

Enums

    " + "

    PointCloud2

    # Copyright DB InfraGO AG and contributors
    # SPDX-License-Identifier: CC0-1.0
    #
    # This message holds a collection of N-dimensional points, which may
    # contain additional information such as normals, intensity, etc. The
    # point data is stored as a binary blob, its layout described by the
    # contents of the \"fields\" array.
    #
    # The point cloud data may be organized 2d (image-like) or 1d (unordered).
    # Point clouds organized as 2d images may be produced by camera depth sensors
    # such as stereo or time-of-flight.

    Fields

    1. std_msgs/Header header
      # Time of sensor data acquisition, and the coordinate frame ID (for 3d points).
    2. uint32 height
      # 2D structure of the point cloud. If the cloud is unordered, height is
      # 1 and width is the length of the point cloud.
    3. uint32 width
      # 2D structure of the point cloud. If the cloud is unordered, height is
      # 1 and width is the length of the point cloud.
    4. PointField[] fields
      # Describes the channels and their layout in the binary data blob.
    5. bool is_bigendian
      # Is this data bigendian?
    6. uint32 point_step
      # Length of a point in bytes
    7. uint32 row_step
      # Length of a row in bytes
    8. uint8[] data
      # Actual point data, size is (row_step*height)
    9. bool is_dense
      # True if there are no invalid points

    Enums

      " ], "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "msgs = MessageDef.from_msg_file(Path(\"data/example_msgs/CameraInfo.msg\"))\n", - "display(msgs)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "

      DiagnosticStatus

      # This message holds the status of an individual component of the robot.

      Fields

      1. DiagnosticStatusLevel level
        # Level of operation enumerated above.
      2. string name
        # A description of the test/component reporting.
      3. string message
        # A description of the status.
      4. string hardware_id
        # A hardware unique string.
      5. KeyValue[] values
        # An array of values associated with the status.

      Enums

      1. DiagnosticStatusLevel
        # Possible levels of operations.
        • byte OK = 0
        • byte WARN = 1
        • byte ERROR = 2
        • byte STALE = 3
      " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "msgs = MessageDef.from_msg_file(Path(\"data/example_msgs/DiagnosticStatus.msg\"))\n", - "display(msgs)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "

      PointCloud2

      # This message holds a collection of N-dimensional points, which may
      # contain additional information such as normals, intensity, etc. The
      # point data is stored as a binary blob, its layout described by the
      # contents of the \"fields\" array.
      #
      # The point cloud data may be organized 2d (image-like) or 1d (unordered).
      # Point clouds organized as 2d images may be produced by camera depth sensors
      # such as stereo or time-of-flight.

      Fields

      1. std_msgs/Header header
        # Time of sensor data acquisition, and the coordinate frame ID (for 3d points).
      2. uint32 height
        # 2D structure of the point cloud. If the cloud is unordered, height is
        # 1 and width is the length of the point cloud.
      3. uint32 width
        # 2D structure of the point cloud. If the cloud is unordered, height is
        # 1 and width is the length of the point cloud.
      4. PointField[] fields
        # Describes the channels and their layout in the binary data blob.
      5. bool is_bigendian
        # Is this data bigendian?
      6. uint32 point_step
        # Length of a point in bytes
      7. uint32 row_step
        # Length of a row in bytes
      8. uint8[] data
        # Actual point data, size is (row_step*height)
      9. bool is_dense
        # True if there are no invalid points

      Enums

        " - ], - "text/plain": [ - "" + "" ] }, "metadata": {}, diff --git a/pyproject.toml b/pyproject.toml index 704b9af..a93c430 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,9 +41,14 @@ Documentation = "https://dsd-dbs.github.io/capella-ros-tools" [project.optional-dependencies] docs = [ "furo", - "sphinx", + "ipython", + "nbsphinx", "sphinx-copybutton", "tomli; python_version<'3.11'", + "jinja2", + "pyyaml>=6.0", + "sphinx!=7.2.0,!=7.2.1,!=7.2.2", + "sphinx-argparse-cli", ] test = [