Overview

My programming may be inadequate to the task.

— Lt. Cmdr. Data

We’re all more than the sum of our parts, Data.
You’ll have to be more than the sum of your programming.

— Counselor Deanna Troi
Star Trek - The Next Generation: In Theory

This document describes the JSON files used by the Core system to define all its main actors:

  • messages

  • nodes

  • configurations

  • packages

  • modules

  • build targets

References

Each JSON file has it’s own Apache AVRO [1] schema.

Package

Overview

A Core package is a bundle of reusable code and meta-data.

A package can include:

  • message definitions

  • node definitions

  • node source code

  • node configuration definitions

  • …​ other code

Packages are processed by the CorePackage.py and CoreWorkspace.py command line tool.

From a Core user point of view a package is nothing but a directory that meets the following requirements:

  1. it contains a file named CORE_PACKAGE.json

  2. it contains a license file LICENSE

  3. it have a well defined structure

CORE_PACKAGE.json

The CORE_PACKAGE.json file describes the package, and it is used by the Core build system either to manage the dependencies or to generate the makefiles.

The file must be valid according to an Apache Avro [1] schema.

Example: CORE_PACKAGE.json file for led package
1
2
3
4
5
{
  "name": "led", (1)
  "description": "LED Nodes", (2)
  "provider": "core" (3)
}
1 the name of the message field must match the name of the package directory
2 a brief description
3 provider is used to avoid a name clash between packages

provider is used to generate a namespace that will enclose all the code generated from the contents of the package (i.e.: core::led).
The same name is used to keep the header files well separated (i.e.: #include <core/led/Publisher.hpp>).

For the packages provided by Nova Labs, it will be core.

Package directory structure

There are two kinds of packages, depending on their content: messages or non-messages.

It is surely possible to have messages in a non-messages package, but this is strongly discouraged.

Messages packages

Message packages must contain nothing but message definition files.

These files must be inside a directory called messages.

It is customary that message packages name ends with _msgs.
`led_msgs` Package
led_msgs Package

Non-Messages packages

Non-Messages package must not contain any message definition files.

Source code must be put inside the include and src directories.
Nodes definitions must reside in nodes directory, while node configuration definitions must be in configurations.

`led` Package
led Package

Message

Overview

Messages represent the data type exchanged between publishers and subscribers.

From a Core user perspective messages are defined using a JSON file.
The file must be valid according to an Apache Avro [1] schema.

JSON definition file

Example: Led.json message definition file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
  "name": "Led", (1)
  "description": "Status LED", (2)
  "namespace": "@", (3)
  "fields": [ (4)
    {
      "name": "led", (5)
      "description": "Which led?", (6)
      "type": "UINT32", (7)
      "size": 1 (8)
    },
    {
      "name": "value",
      "description": "Value of the led",
      "type": "UINT8",
      "size": 1
    }
  ]
}
1 the name of the message field must match the name of the JSON file; name must be at most 15 characters long
2 a brief description
3 by default ('@') the namespace will be the name of the package the message resides in; namespace can be used to override it
4 messges cannot have nested data structures
5 field name must be at most 15 characters long
6 a brief description
7 the supported field types are CHAR, INT8, UINT8, INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT32, FLOAT64, TIMESTAMP
8 array type fields are specified using the size attribute, scalar types require to specify 1 as the size of the field

Generated file

Messages are converted to source code using the command line tools. The code generator relies on the C preprocessor to keep the generated code clean and readable.

Example: Source code generated from Led.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#pragma once

#include <core/mw/CoreMessage.hpp>

namespace core {
namespace common_msgs {

CORE_MESSAGE_BEGIN(Led) //Status LED
        CORE_MESSAGE_FIELD(led, UINT32, 1) // Which led
        CORE_MESSAGE_FIELD(value, UINT8, 1) // Value of the led
CORE_MESSAGE_END

}
}

Array type fields will be implemented using core::Array class template (that is a stripped down std::array).

From the source code point of view, messages are classes that inherit from core::mw::Message class.

Node

Overview

Coming soon…​

Configuration

Overview

From a Core user perspective node configurations are defined using a JSON file.
The file must be valid according to an Apache Avro [1] schema.

JSON definition file

Example: LedPublisherConfiguration.json message definition file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
  "name": "LedPublisherConfiguration", (1)
  "description": "LED Publisher node configuration", (2)
  "namespace": "@", (3)
  "fields": [ (4)
    {
      "name": "topic", (5)
      "description": "Name of the topic to publish to", (6)
      "type": "CHAR", (7)
      "size": 16 (8)
    },
    {
      "name": "led",
      "description": "Which led",
      "type": "UINT32",
      "size": 1
    }
  ]
}
1 the name of the message field must match the name of the JSON file; name must be at most 15 characters long
2 a brief description
3 by default ('@') the namespace will be the name of the package the configuration resides in; namespace can be used to override it
4 configurations cannot have nested data structures
5 field name must be at most 15 characters long
6 a brief description
7 the supported field types are CHAR, INT8, UINT8, INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT32, FLOAT64, TIMESTAMP
8 array type fields are specified using the size attribute, scalar types require to specify 1 as the size of the field

Generated file

Configurations are converted to source code using the command line tools. The code generator relies on the C preprocessor to keep the generated code clean and readable.

Example: Source code generated from LedPublisherConfiguration.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#pragma once

#include <core/mw/CoreConfiguration.hpp>

namespace core {
namespace led {

CORE_CONFIGURATION_BEGIN(PublisherConfiguration) //LED Publisher node configuration
        CORE_CONFIGURATION_FIELD(topic, CHAR, 16) // Name of the topic to publish to
        CORE_CONFIGURATION_FIELD(led, UINT32, 1) // Which led
CORE_CONFIGURATION_MAP_BEGIN(2)
        CORE_CONFIGURATION_MAP_ENTRY(PublisherConfiguration, topic)
        CORE_CONFIGURATION_MAP_ENTRY(PublisherConfiguration, led)
CORE_CONFIGURATION_MAP_END()
CORE_CONFIGURATION_SIGNATURE(0x73fc44ae)
CORE_CONFIGURATION_END()

}
}

Array type fields will be implemented using core::Array class template (that is a stripped down std::array).

From the source code point of view, messages are classes that inherit from core::mw::CoreConfiguration class.

Module

Overview

Coming soon…​

Workspace

Overview

A workspace comprehends:

  • build targets

  • custom packages

  • custom modules

Workspaces are processed by the CoreWorkspace.py command line tool.

From a Core user point of view a workspace is nothing but a directory that meets the following requirements:

  1. it contains a file named WORKSPACE.json

  2. it have a well defined structure

WORKSPACE.json

The WORKSPACE.json file describes the workspace.

The file must be valid according to an Apache Avro [1] schema.

Example: WORKSPACE.json file
1
2
3
4
{
  "name": "Workspace", (1)
  "description": "Testing Workspace", (2)
}
1 the name of the workspace field must match the name of the package directory
2 a brief description

Module Target

Overview

Module Target represent the firmware that will be compiled and deployed to a module.

Module Targets are processed by the CoreWorkspace.py command line tool.

From a Core user point of view a target is nothing but a directory that meets the following requirements:

  1. it contains a file named MODULE_TARGET.json

  2. it have a well defined structure

MODULE_TARGET.json

Example: MODULE_TARGET.json for an IMU target
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  "name": "imu", (1)
  "description": "IMU", (2)
  "module": "imu", (3)
  "required_packages": [ (4)
    "led",
    "balancing_robot",
    "actuator_msgs",
    "differential_drive_msgs",
    "sensor_msgs",
    "pid",
    "pid_msgs"
  ],
  "sources": [ (5)
    "main.cpp"
  ],
  "includes": [] (6)
}
1 the name of the target; it will be used as the module name
2 a brief description
3 the module on which the firmware will run
4 list of required packages
5 list of source files to be compiled (relative to target directory)
6 list of include directories for header files (relative to target directory)

Generated file

Module Target are converted to CMake [2] code using the command line tools.

Example: CMakeLists.txt generated from MODULE_TARGET.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Generated by ModuleTarget.py
# Remove a "#" from the line below to stop generating this file every time you call CoreWorkspace generate
## TARGET MODULE imu (1)

PROJECT( imu )
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

FIND_PACKAGE( CORE_BUILD CONFIG REQUIRED )

INCLUDE ( CoreTarget NO_POLICY_SCOPE )
SET( PROJECT_INCLUDE_DIRECTORIES
)

SET( PROJECT_SOURCES
  main.cpp
)

core_target_module(
  MODULE imu
  PACKAGES
    led
    balancing_robot
    actuator_msgs
    differential_drive_msgs
    sensor_msgs
    pid
    pid_msgs
)
1 remove a # to stop generating this file every time you call CoreWorkspace generate
CMakeLists.txt file is generated every time if it contains a line starting with ## TARGET MODULE

1. Apache AVRO https://avro.apache.org/
2. CMake https://cmake.org/