# codb2json

## Introduction

`codb2json` is a command-line utility designed to convert CANopen Profile
data base (codb) files to JSON format.  The idea is to provide a more modern
and easy-to-process file format that can be used to check the conformity of
a CANopen CC interface using [CANopenTerm](https://canopenterm.de).

## Description

In order to test if the CANopen CC interface of a device is conform to
CANopen application layer and communication profile (CiA 301), CiA
consortium has developed the
[CANopen CC conformance test tool](https://www.can-cia.org/services/canopen-conformance-test-tool).

As this tool is primarily a proprietary Windows tool and there are no
alternatives on the market, I would like to extend
[CANopenTerm](https://canopenterm.de) with a similar feature.

To check the conformity of a CANopen CC interface, the CiA tool
uses the so-called CANopen Profile data base (codb) format.
This format is not very well documented, antiquated, and somewhat
difficult to process.

That's whyIe developed a converter that generates the same
information in JSON format. JSON is a robust, easy-to-process,
and widely accepted file format.

## Why reinvent the wheel?

Although the CODB file format is very compact, it is very
difficult to maintain and prone to errors.  I believe that a
database used specifically for conformance testing must be
syntactically testable.  Therefore, a fresh view on the file
format is necessary to ensure ease of maintenance and error
reduction.

## Installation

`codb2json` is part of CANopenTerm.

## Usage

The `codb2json` tool is a command-line utility that converts a
CANopen Profile data base (codb) file to JSON format.  It takes
the path to the CODB file as input and generates a corresponding
JSON file as output:

```bash
./codb2json v301.codb
```

The resulting JSON file will have the same name as the input
file, but with the `.json` extension.

## File Format Specification

The JSON format generated by `codb2json` follows a structured schema to represent CANopen Profile data base (codb) files. Below is the detailed specification of the JSON format:

<!-- tabs:start -->
<!-- tab: Root Structure -->
The root of the JSON file is an array of objects, each representing a CANopen object dictionary entry.

```json
[
  {
    "id": "1000",
    "index": 4096,
    "sub_indices": [...],
    "code": {...},
    "min_elements": {...},
    "max_elements": {...},
    "desc": "Device Type",
    "kind": 1
  }, ...
]
```

<!-- tab: Entry -->
Each object dictionary entry contains the following fields:

- `id` (string): The hexadecimal identifier of the object, main index.
- `index` (integer): The decimal index of the object.
- `sub_indices` (array): An array of sub-index objects.
- `code` (object): An object representing the object code attributes.
- `min_elements` (object): An object representing the minimum elements attributes.
- `max_elements` (object): An object representing the maximum elements attributes.
- `desc` (string): A description of the object.
- `kind` (integer): The kind of the sub-index, one of the following:
    - `0`: OPTIONAL
    - `1`: MANDATORY
    - `2`: CONDITIONAL

<!-- tab: Sub-Index -->
Each sub-index object contains the following fields:

- `index` (integer): The sub-index number.
- `desc` (string): A description of the sub-index.
- `kind` (integer): The kind of the sub-index, one of the following:
    - `0`: OPTIONAL
    - `1`: MANDATORY
    - `2`: CONDITIONAL
- `data_type` (object): An object representing the data type attributes.
- `access_type` (object): An object representing the access type attributes.
- `low_limit` Low limit.
- `high_limit` High limit.
- `default_value` Default value.
- `mappable` Is mappable.

<!-- tab: Code -->
The code object contains the following fields:

- `type` (integer): The type of the code, one of the following:
    - `0`: DOMAIN,
    - `1`: DEFTYPE,
    - `2`: DEFSTRUCT,
    - `3`: VAR,
    - `4`: ARRAY
    - `5`: RECORD
- `attr` (integer): The attribute of the code.
    - `0`: Value, if exists, is a default and can be changed in the range specified by the data type.
    - `1`: Value is mandatory and cannot be changed.
    - `2`: Value is a default and can be changed in the range specified by the data type.
    - `3`: Value is a default and can be changed within the limits.
    - `4`: Not applicable for the preceding data field, data field shall be empty.
- `lower` (integer): The lower bound of the code.
- `upper` (integer): The upper bound of the code.

<!-- tab: Min Elements -->
The min elements object contains the following fields:

- `value` (integer): The minimum number of elements, 0 to 255.
- `attr` (integer): The attribute of the minimum elements.
    - `0`: Value, if exists, is a default and can be changed in the range specified by the data type.
    - `1`: Value is mandatory and cannot be changed.
    - `2`: Value is a default and can be changed in the range specified by the data type.
    - `3`: Value is a default and can be changed within the limits.
    - `4`: Not applicable for the preceding data field, data field shall be empty.
- `lower` (integer): The lower bound of the minimum elements.
- `upper` (integer): The upper bound of the minimum elements.

<!-- tab: Max Elements -->
The max elements object contains the following fields:

- `value` (integer): The maximum number of elements, 0 to 255.
- `attr` (integer): The attribute of the maximum elements.
    - `0`: Value, if exists, is a default and can be changed in the range specified by the data type.
    - `1`: Value is mandatory and cannot be changed.
    - `2`: Value is a default and can be changed in the range specified by the data type.
    - `3`: Value is a default and can be changed within the limits.
    - `4`: Not applicable for the preceding data field, data field shall be empty.
- `lower` (integer): The lower bound of the maximum elements.
- `upper` (integer): The upper bound of the maximum elements.

<!-- tab: Data Type -->
The data type object contains the following fields:

- `type` (integer): The type of the data, one of the following:
    - `0`: None/Unspecified
    - `1`: BOOLEAN,
    - `2`: INTEGER8,
    - `3`: UNSIGNED8,
    - `4`: INTEGER16,
    - `5`: UNSIGNED16,
    - `6`: INTEGER24,
    - `7`: UNSIGNED24,
    - `8`: INTEGER32,
    - `9`: UNSIGNED32,
    - `10`: INTEGER48,
    - `11`: UNSIGNED48,
    - `12`: INTEGER56,
    - `13`: UNSIGNED56,
    - `14`: INTEGER64,
    - `15`: UNSIGNED64,
    - `16`: REAL32,
    - `17`: REAL64,
    - `18`: FLOAT,
    - `19`: TIME_OF_DAY,
    - `20`: VISIBLE_STRING,
    - `21`: OCTET_STRING,
    - `22`: DOMAIN
- `attr` (integer): The attribute of the data.
    - `0`: Value, if exists, is a default and can be changed in the range specified by the data type.
    - `1`: Value is mandatory and cannot be changed.
    - `2`: Value is a default and can be changed in the range specified by the data type.
    - `3`: Value is a default and can be changed within the limits.
    - `4`: Not applicable for the preceding data field, data field shall be empty.
- `lower` (integer): The lower bound of the data.
- `upper` (integer): The upper bound of the data.

<!-- tab: Access Type -->
The access type object contains the following fields:

- `type` (integer): The type of the access, one of the following:
    - `0`: unspecified
    - `1`: const
    - `2`: ro
    - `3`: wo
    - `4`: rw
    - `5`: wwr
    - `6`: rww
- `attr` (integer): The attribute of the access.
    - `0`: Value, if exists, is a default and can be changed in the range specified by the data type.
    - `1`: Value is mandatory and cannot be changed.
    - `2`: Value is a default and can be changed in the range specified by the data type.
    - `3`: Value is a default and can be changed within the limits.
    - `4`: Not applicable for the preceding data field, data field shall be empty.
- `lower` (integer): The lower bound of the access.
- `upper` (integer): The upper bound of the access.

<!-- tab: Low Limit -->
The low limit object contains the following fields:

- `value` (integer): The lower limit value.
- `attr` (integer): The attribute of the lower limit.
    - `0`: Value, if exists, is a default and can be changed in the range specified by the data type.
    - `1`: Value is mandatory and cannot be changed.
    - `2`: Value is a default and can be changed in the range specified by the data type.
    - `3`: Value is a default and can be changed within the limits.
    - `4`: Not applicable for the preceding data field, data field shall be empty.
- `lower` (integer): The lower bound of the lower limit.
- `upper` (integer): The upper bound of the lower limit.

<!-- tab: High Limit -->
The high limit object contains the following fields:

- `value` (integer): The upper limit value.
- `attr` (integer): The attribute of the upper limit.
    - `0`: Value, if exists, is a default and can be changed in the range specified by the data type.
    - `1`: Value is mandatory and cannot be changed.
    - `2`: Value is a default and can be changed in the range specified by the data type.
    - `3`: Value is a default and can be changed within the limits.
    - `4`: Not applicable for the preceding data field, data field shall be empty.
- `lower` (integer): The lower bound of the upper limit.
- `upper` (integer): The upper bound of the upper limit.

<!-- tab: Default -->
The default value object contains the following fields:

- `value` (integer): The default value.
- `attr` (integer): The attribute of the default value.
    - `0`: Value, if exists, is a default and can be changed in the range specified by the data type.
    - `1`: Value is mandatory and cannot be changed.
    - `2`: Value is a default and can be changed in the range specified by the data type.
    - `3`: Value is a default and can be changed within the limits.
    - `4`: Not applicable for the preceding data field, data field shall be empty.
- `lower` (integer): The lower bound of the default value.
- `upper` (integer): The upper bound of the default value.

<!-- tab: Mappable -->
The mappable object contains the following fields:

- `value` (boolean): The mappable value.
- `attr` (integer): The attribute of the mappable value.
    - `0`: Value, if exists, is a default and can be changed in the range specified by the data type.
    - `1`: Value is mandatory and cannot be changed.
    - `2`: Value is a default and can be changed in the range specified by the data type.
    - `3`: Value is a default and can be changed within the limits.
    - `4`: Not applicable for the preceding data field, data field shall be empty.
- `lower` (integer): The lower bound of the mappable value.
- `upper` (integer): The upper bound of the mappable value.
<!-- tabs:end -->

### Example

<!-- tabs:start -->
<!-- tab:Input CODB file -->
```plaintext
1000::Device Type:mandatory::VAR:m:UNSIGNED32:m:ro:d[const,ro]::n::n:::::::n:m

$1001=1000

1003::Predefined Error Field:optional::ARRAY:m::m::m:2:m:255:m::m::m::m::m
1003:00:Number of Errors:optional::VAR:m:UNSIGNED8:m:rw:d[rw,ro]::n::n:::::0:m:n:m
1003:01:Standard Error Field:optional::VAR:m:UNSIGNED32:m:ro:m::n::n::::d:0:m:n:m
```
<!-- tab:Output JSON file -->
Below is an example of a JSON file generated by `codb2json`:

```json
[
  {
    "id": "1000",
    "index": 4096,
    "sub_indices": [
      {
        "index": 0,
        "desc": "Device Type",
        "kind": 1,
        "data_type": {
          "type": 9,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "access_type": {
          "type": 2,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "low_limit": {
          "value": 0,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "high_limit": {
          "value": 0,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "default_value": {
          "value": 0,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "mappable": {
          "value": false,
          "attr": 0,
          "lower": 0,
          "upper": 0
        }
      }
    ],
    "code": {
      "type": 3,
      "attr": 1,
      "lower": 0,
      "upper": 0
    },
    "min_elements": {
      "value": 0,
      "attr": 0,
      "lower": 0,
      "upper": 0
    },
    "max_elements": {
      "value": 0,
      "attr": 0,
      "lower": 0,
      "upper": 0
    },
    "desc": "Device Type",
    "kind": 1
  },
  {
    "id": "1001",
    "index": 4097,
    "sub_indices": [
      {
        "index": 0,
        "desc": "Device Type",
        "kind": 1,
        "data_type": {
          "type": 9,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "access_type": {
          "type": 2,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "low_limit": {
          "value": 0,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "high_limit": {
          "value": 0,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "default_value": {
          "value": 0,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "mappable": {
          "value": false,
          "attr": 0,
          "lower": 0,
          "upper": 0
        }
      }
    ],
    "code": {
      "type": 3,
      "attr": 1,
      "lower": 0,
      "upper": 0
    },
    "min_elements": {
      "value": 0,
      "attr": 0,
      "lower": 0,
      "upper": 0
    },
    "max_elements": {
      "value": 0,
      "attr": 0,
      "lower": 0,
      "upper": 0
    },
    "desc": "Device Type",
    "kind": 1
  },
  {
    "id": "1003",
    "index": 4099,
    "sub_indices": [
      {
        "index": 0,
        "desc": "Number of Errors",
        "kind": 0,
        "data_type": {
          "type": 3,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "access_type": {
          "type": 4,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "low_limit": {
          "value": 0,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "high_limit": {
          "value": 0,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "default_value": {
          "value": 0,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "mappable": {
          "value": false,
          "attr": 0,
          "lower": 0,
          "upper": 0
        }
      },
      {
        "index": 1,
        "desc": "Standard Error Field",
        "kind": 0,
        "data_type": {
          "type": 9,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "access_type": {
          "type": 2,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "low_limit": {
          "value": 0,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "high_limit": {
          "value": 0,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "default_value": {
          "value": 0,
          "attr": 0,
          "lower": 0,
          "upper": 0
        },
        "mappable": {
          "value": false,
          "attr": 0,
          "lower": 0,
          "upper": 0
        }
      }
    ],
    "code": {
      "type": 4,
      "attr": 1,
      "lower": 0,
      "upper": 0
    },
    "min_elements": {
      "value": 2,
      "attr": 0,
      "lower": 0,
      "upper": 0
    },
    "max_elements": {
      "value": 255,
      "attr": 0,
      "lower": 0,
      "upper": 0
    },
    "desc": "Predefined Error Field",
    "kind": 0
  }
]
```
<!-- tabs:end -->
