Skip to content

DimaRU/CDRCodable

Repository files navigation

CDRCodable

Build

A OMG Common Data Representation (CDR) (PLAIN_CDR) encoder and decoder for Swift Codable types.

Now can be used with FastRTPSSwift, a Swift wrapper for eProsima FastDDS library.

Use msg2swift to automatically generate Swift models from ROS .msg files.

Requirements

  • Swift 6.2+

Usage

Encoding Messages

import CDRCodable

let encoder = CDREncoder()
let value = try! encoder.encode("hello")
// Data([6, 0, 0, 0, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0, 0, 0])

Decoding Messages

import CDRCodable

let decoder = CDRDecoder()
let data = Data([3, 0, 0, 0, 1, 0, 2, 0, 3, 0])
let value = try! decoder.decode([Int16].self, from: data)
// [1, 2, 3]

Installation

Swift Package Manager

Add the CDRCodable package to your target dependencies in Package.swift:

.package(url: "https://github.com/DimaRU/CDRCodable", from: "2.0.0"),

Supported IDL/ROS types

1. Primitive types

The following table shows the basic IDL types supported by CDRCodable and how they are mapped to Swift and C++11.

Swift C++11 ROS IDL
Int8 char int8 char
UInt8 uint8_t uint8 octet
Int16 int16_t int16 short
UInt16 uint16_t uint16 unsigned short
Int32 int32_t int32 long
UInt32 uint32_t uint32 unsigned long
Int64 int64_t int64 long long
UInt64 uint64_t uint64 unsigned long long
Float float float32 float
Double double float64 double
Bool bool bool boolean
String std::string string string

2. Arrays

Starting from version 2.0.0 CDRCodable supports fixed-size arrays by native way using the new InlineArray type in Swift 6.2. Sample for sensor_msgs/CameraInfo:

//
// CameraInfo.swift
//
// This file was generated from ROS message file using msg2swift.
//

struct CameraInfo: Codable {
    let header: Header
    let height: UInt32
    let width: UInt32
    let distortionModel: String
    let d: [Double]
    let k: [9 of Double]
    let r: [9 of Double]
    let p: [12 of Double]
    let binningX: UInt32
    let binningY: UInt32
    let roi: RegionOfInterest
}

3. Sequences

CDRCodable supports sequences, which map between Swift Array and C++ std::vector container. The following table represents how the map between Swift, C++11 and IDL and is handled.

Swift C++11 IDL
Data std::vector<uint8_t> sequence<octet>
Array<Int8> std::vector<char> sequence<char>
Array<UInt8> std::vector<uint8_t> sequence<octet>
Array<Int16> std::vector<int16_t> sequence<short>
Array<UInt16> std::vector<uint16_t> sequence<unsigned short>
Array<Int32> std::vector<int32_t> sequence<long>
Array<UInt32> std::vector<uint32_t> sequence<unsigned long>
Array<Int64> std::vector<int64_t> sequence<long long>
Array<UInt64> std::vector<uint64_t> sequence<unsigned long long>
Array<Float> std::vector<float> sequence<float>
Array<Double> std::vector<double> sequence<double>
Array<Bool> std::vector<bool> sequence<boolean>
Array<String> std::vector<std::string> sequence<string>

4. Enumerations

Swift IDL
enum e: Int32 enum e

Example: IDL definition:

enum ESubsystemState
{
    UNKNOWN         = 0,
    INITIALIZED     = 1,
    POSTING         = 2,
    ACTIVE          = 3,
    STANDBY         = 4,
    RECOVERY        = 5,
    DISABLED        = 6
};

Swift:

enum ESubsystemState: Int32, Codable {
    case unknown         = 0
    case initialized     = 1
    case posting         = 2
    case active          = 3
    case standby         = 4
    case recovery        = 5
    case disabled        = 6
}

5. Union

Union type is not supported by CDRCodable directly and needed custom coding. Example:

IDL definition:

union ControlUnion switch (unsigned long)
{
    case CONTROL_TYPE_S8:
        char value_int8;
    case CONTROL_TYPE_S16:
        short value_int16;
    case CONTROL_TYPE_S32:
        long value_int32;
    case CONTROL_TYPE_S64:
        long long value_int64;
    case CONTROL_TYPE_U8:
        octet value_uint8;
    case CONTROL_TYPE_U16:
        unsigned short value_uint16;
    case CONTROL_TYPE_U32:
        unsigned long value_uint32;
    case CONTROL_TYPE_U64:
        unsigned long long value_uint64;
    case CONTROL_TYPE_BITMASK:
        unsigned long value_bitmask;
    case CONTROL_TYPE_BUTTON:
        boolean value_button;
    case CONTROL_TYPE_BOOLEAN:
        boolean value_boolean;
    case CONTROL_TYPE_STRING:
        string<128> value_string;
    case CONTROL_TYPE_STRING_MENU:
        unsigned long value_string_menu;
    case CONTROL_TYPE_INT_MENU:
        unsigned long value_int_menu;
};

Swift

enum ControlUnion: UInt32, Codable {
    case S8(value: Int8) = 0
    case S16(value: Int16)
    case S32(value: Int32)
    case S64(value: Int64)
    case U8(value: UInt8)
    case U16(value: UInt16)
    case U32(value: UInt32)
    case U64(value: UInt64)
    case Bitmask(bitmask: UInt32)
    case Button(button: Bool)
    case Boolean(value: Bool)
    case StringValue(value: String)
    case StringMenu(stringMenu: UInt32)
    case IntMenu(intMenu: UInt32)
    
    init(from decoder: Decoder) throws {
        var container = try decoder.singleValueContainer()
        let selector = try container.decode(UInt32.self)
        switch selector {
            case 0:
                let value = try container.decode(Int8.self)
                self = .S8(value: value)
            case 1:
                let value = try container.decode(Int16.self)
                self = .S16(value: value)
            case 2:
                let value = try container.decode(Int32.self)
                self = .S32(value: value)
            case 3:
                let value = try container.decode(Int64.self)
                self = .S64(value: value)
            case 4:
                let value = try container.decode(UInt8.self)
                self = .U8(value: value)
            case 5:
                let value = try container.decode(UInt16.self)
                self = .U16(value: value)
            case 6:
                let value = try container.decode(UInt32.self)
                self = .U32(value: value)
            case 7:
                let value = try container.decode(UInt64.self)
                self = .U64(value: value)
            case 8:
                let value = try container.decode(UInt32.self)
                self = .Bitmask(bitmask: value)
            case 9:
                let value = try container.decode(Bool.self)
                self = .Button(button: value)
            case 10:
                let value = try container.decode(Bool.self)
                self = .Boolean(value: value)
            case 11:
                let value = try container.decode(String.self)
                self = .StringValue(value: value)
            case 12:
                let value = try container.decode(UInt32.self)
                self = .StringMenu(stringMenu: value)
            case 13:
                let value = try container.decode(UInt32.self)
                self = .IntMenu(intMenu: value)
            default:
                let error = DecodingError.dataCorruptedError(in: container, debugDescription: "Illegal union selector \(selector)")
                throw error
        }
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(self.rawValue)
        switch self {
        case .S8(value: let value): try container.encode(value)
        case .S16(value: let value): try container.encode(value)
        case .S32(value: let value): try container.encode(value)
        case .S64(value: let value): try container.encode(value)
        case .U8(value: let value): try container.encode(value)
        case .U16(value: let value): try container.encode(value)
        case .U32(value: let value): try container.encode(value)
        case .U64(value: let value): try container.encode(value)
        case .Bitmask(bitmask: let bitmask): try container.encode(bitmask)
        case .Button(button: let button): try container.encode(button)
        case .Boolean(value: let value): try container.encode(value)
        case .StringValue(value: let value): try container.encode(value)
        case .StringMenu(stringMenu: let stringMenu): try container.encode(stringMenu)
        case .IntMenu(intMenu: let intMenu): try container.encode(intMenu)
        }
    }
}

6. Struct

struct may be coded as Swift struct or class. Example:

IDL definition:

struct ControlTarget
{
    string id;
    
    float pitch;
    float yaw;
    float thrust;
    float lift;
};
struct ControlTarget: Codable
{
    let id: String

    let pitch: Float
    let yaw: Float
    let thrust: Float
    let lift: Float
}

License

MIT

About

An OMG Common Data Representation (CDR) encoder and decoder for Swift `Codable` types.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

Contributors

Languages