Shared Data Types

The communication database allows to define data types which can be used in the receiver and the sender. They allow to define a way to pack multiple individual datapoint into a structure and to define enumeration types and thus restrict possible values for a type. They allow to build message objects that transmit complex and nested data structures, and they even allow to define the base message structure (see Special types).

Definition

They are defined in the sharedDataTypes.json file. The file consists of a json object, whose attributes are the shared data types. The value of these attributes describes information about the type of the shared data type.

In the following example, there are three data types defined, a, b and c. The attributes of these data types are empty for this example, in a real example the {} wouldn’t be valid:

{
  "a": {},
  "b": {},
  "c": {}
}

Type description

The value for each attribute of the top level json object describes the type of this shared data type. It can be either a string or an object.

Simple

In the simple case, the type description is a string. In this case, the string is the name of the base type. The base type can also be a unit type. Additionally, the base type can also be a shared data type that was declared before the new type.

In the following example, the a type is an 8-bit unsigned integer, the b type is a meter unit and the c type is synonym for the a type and therefore also an 8-bit unsigned integer:

{
  "a": "uint8",
  "b": "meter",
  "c": "a"
}
Arrays

The simple data type description can have an opening bracket, then a number or a name of a shared constant which resolves to a positive integer number, and then a closing bracket. This indicates that the type represents an array, or a list.

in the following example, two types are declared: The type a, which represents a list of 5 unsigned 8-bit integers, and the type b, which also represents a list of unsigned 8-bit integers whose size depends on the value of the shared constant B_LENGTH.

{
  "a": "uint8[5]",
  "b": "uint8[B_LENGTH]"
}

Advanced

A type can be enhanced with additional metadata. To add metadata to a type from the simple case example, the value must become an object and the string name of the base type moves into the __type__ attribute of the new object. This allows to represent the following metadata about the type:

Attribute Name

Description

__type__

The base type of the shared type, has the same restrictions as in the simple case.

__doc__

An optional human readable description of this type.

__value__

An optional default value for this type. This is used for example in telecommands when the value is not provided.

The following example shows the a type from the simple example with a description and a default value:

{
  "a": {
    "__doc__": "A description of this type.",
    "__type__": "uint8",
    "__value__": 1
  }
}
Enumerations

If a __values__ attribute is given (note the s at the end), the type is interpreted as an enumeration type. These types have a restricted set of named values called enumeration members, that the type can represent.

The enumeration members are automatically assigned integer values based on the C standard for enumeration default values: If no explicit value is defined for the first member, it is assigned the value 0. Fore each other member, if the no explicit value is specified, it’s value becomes the value of the previous member incremented by 1. The value of an enumeration member can be specified via its __value__ attribute. If two enumeration members have the same value, they are the second is considered an alias of the first. If serialized, both values will be deserialized to the first member.

If a __type__ attribute is given, it represents the underlying base type of the enumeration type. If it is omitted, the smallest possible base type is automatically chosen based on the largest enumeration member value.

These named values are either provided in a list, in which case the list item must be a string representing the name of that enumeration member, or as an object where each value is represented as an attribute of that object. The name of each attribute is the name of that enumeration member, and the value must be an object with additional metadata about this enumeration value:

Attribute Name

Description

__doc__

An optional description of this enumeration value.

__value__

An optional explicit value of this enumeration value.

In the following example, two enumerations are defined: a has two enumeration members, both of which have a documentation. The first member has the value a.A_STATE_1 = 0, the second member has the value a.A_STATE_2 = 42. b has also two enumeration members, but none of them have a documentation. The first member has the value b.B_STATE_1 = 0, the second member has the value b.B_STATE_2 = 1.

{
  "a": {
    "__doc__": "A description of the a type.",
    "__type__": "uint8",
    "__values__": {
      "A_STATE_1": {
        "__doc__": "Description for the A_STATE_1 value."
      },
      "A_STATE_2": {
        "__doc__": "Description for the A_STATE_2 value.",
        "__value__": 42
      }
    }
  },
  "b": {
    "__doc__": "A description of the b type.",
    "__type__": "uint8",
    "__values__": [
      "B_STATE_1",
      "B_STATE_2"
    ]
  }
}
Structure types

Structural datatypes allow for grouping of and nesting of multiple datatypes. A structural type can include members of any other data type that was defined before, or of a new substructure.

Structural types can’t have a base type, therefore they don’t allow to specify a __type__ attribute. They do allow to specify a __doc__ documentation description. All other attributes of the metadata are interpreted as children of the structural type. The name of the attributes are the names of the children. If a new type is defined in the child, it’s going to have the same name as the attribute. The value of each child attribute is a new type description. Circular types are not possible, because the type can’t reference itself.

The following example defines two structural types: The first one a has two children, an unsigned 8-bit integer x and a character y. The second type b has a documentation and three children: A boolean element1 and an element of the previously defined type a, which has the two mentioned children. Additionally, b has a third child named element3, which is a structural type whose name is also element3 with two children, a 32-bit signed integer subElement1 and an array of four bytes named subElement2.

{
  "a": {
    "x": "uint8",
    "y": "char"
  },
  "b": {
    "__doc__": "A description of this type.",
    "element1": "bool",
    "element2": "a",
    "element3": {
      "subElement1": "int32",
      "subElement2": "bytes[4]"
    }
  }
}

Special types

There are some special types that are automatically defined by the database and some that are expected to be defined, for the serializer and parser to function properly. The later are:

  • TelemetryMessageHeader: The parser expects a structural type with this name (can be configured when constructing the parser). The type must define an integer sync byte 1, sync byte 2, as well as an type child of the automatically generated TelemetryType enum type.

  • TelecommandMessageHeader: The serializer expects a structural type with this name (can also be configured when constructing the serializer). The type must have an integer sync byte 1, sync byte 2, as well as a type member which is of the automatically generated enum type TelecommandType.