Task 617: .ROB File Format

Task 617: .ROB File Format

1. List of all the properties of this file format intrinsic to its file system

The .rob file format is a text-based format used in the Klamp't robotics library for defining robot models. It consists of key-value lines where each key represents a property, followed by its values. Properties are intrinsic to the format's structure for specifying kinematics, dynamics, geometry, joints, drivers, and other robot attributes. Based on the specifications, the complete list of properties is:

  • links: Specifies the names of the robot's links.
  • parents: Specifies the parent indices for each link.
  • jointtype: Specifies the type of joint for each degree of freedom (DOF), e.g., 'r' for revolute or 'p' for prismatic.
  • tparent: Specifies the relative rigid transforms between each link and its parent (rotation matrix and translation).
  • alpha, a, d, theta: Denavit-Hartenberg (D-H) parameters for joint configuration.
  • alphadeg, thetadeg: D-H parameters in degrees.
  • axis: Specifies the axis of rotation or translation for each DOF.
  • qmin, qmindeg: Lower limits for joint configurations (in radians or degrees).
  • qmax, qmaxdeg: Upper limits for joint configurations (in radians or degrees).
  • q, qdeg: Initial joint configuration values (in radians or degrees).
  • translation: Shifts the base link's position.
  • rotation: Rotates the base link using a rotation matrix.
  • scale: Scales the entire robot model.
  • mount: Mounts a sub-robot or geometry to a link, with optional transform and name prefix.
  • mass: Masses for each link.
  • automass: Automatically computes mass properties from geometry, with optional surface fraction.
  • com: Centers of mass for each link.
  • inertiadiag: Diagonal elements of inertia matrices (assuming zero off-diagonals).
  • inertia: Full 3x3 inertia matrices for each link.
  • velmin, velmindeg: Lower velocity limits (in radians/s or degrees/s).
  • velmax, velmaxdeg: Upper velocity limits (in radians/s or degrees/s).
  • accmax, accmaxdeg: Absolute acceleration limits (in radians/s² or degrees/s²).
  • torquemax: Torque limits for each DOF.
  • powermax: Power limits for each DOF.
  • autotorque: Automatically sets torque limits based on descendant links.
  • geometry: File paths to geometry files for each link.
  • geomscale: Scales for link geometries.
  • geomtransform: 4x4 transformation matrices for link geometries.
  • geommargin: Collision margins for geometries.
  • noselfcollision: Pairs of links to disable self-collision checks.
  • selfcollision: Pairs of links to enable self-collision checks.
  • joint: Defines joint types (e.g., normal, spin, weld, floating) for links or ranges.
  • driver: Defines driver types and parameters.
  • servoP: Position gains for drivers.
  • servoI: Integral gains for drivers.
  • servoD: Derivative gains for drivers.
  • dryFriction: Dry friction coefficients for drivers.
  • viscousFriction: Viscous friction coefficients for drivers.
  • property: Custom properties like sensors or controller, specified as files or XML strings.

These properties are parsed from lines in the file, with support for comments (#), line continuations (), and quoted strings for values with spaces.

3. Ghost blog embedded html javascript that allows a user to drag n drop a file of format .ROB and it will dump to screen all these properties

Drag and drop a .rob file here

4. Python class that can open any file of format .ROB and decode read and write and print to console all the properties from the above list

import json
import os

class RobFile:
    def __init__(self):
        self.properties = {}

    def load(self, filename):
        if not os.path.exists(filename):
            raise FileNotFoundError(f"File {filename} not found")
        with open(filename, 'r') as f:
            text = f.read()
        lines = text.split('\n')
        current_line = ''
        for line in lines:
            line = line.strip()
            if line.startswith('#') or not line:
                continue
            if line.endswith('\\'):
                current_line += line[:-1].strip() + ' '
                continue
            current_line += line
            parts = []
            i = 0
            while i < len(current_line):
                if current_line[i] == '"':
                    j = current_line.find('"', i + 1)
                    parts.append(current_line[i+1:j])
                    i = j + 1
                else:
                    j = i
                    while j < len(current_line) and current_line[j] not in ' \t':
                        j += 1
                    parts.append(current_line[i:j])
                    i = j
                while i < len(current_line) and current_line[i] in ' \t':
                    i += 1
            if parts:
                key = parts[0]
                values = parts[1:]
                self.properties[key] = values
            current_line = ''

    def print_properties(self):
        print(json.dumps(self.properties, indent=4))

    def write(self, filename):
        with open(filename, 'w') as f:
            for key, values in self.properties.items():
                line = key
                for value in values:
                    if ' ' in value:
                        line += f' "{value}"'
                    else:
                        line += f' {value}'
                f.write(line + '\n')

# Example usage:
# rob = RobFile()
# rob.load('example.rob')
# rob.print_properties()
# rob.write('output.rob')

5. Java class that can open any file of format .ROB and decode read and write and print to console all the properties from the above list

import java.io.*;
import java.util.*;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class RobFile {
    private Map<String, List<String>> properties = new HashMap<>();

    public void load(String filename) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(filename));
        String line;
        StringBuilder currentLine = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            line = line.trim();
            if (line.startsWith("#") || line.isEmpty()) continue;
            if (line.endsWith("\\")) {
                currentLine.append(line.substring(0, line.length() - 1).trim()).append(" ");
                continue;
            }
            currentLine.append(line);
            List<String> parts = parseLine(currentLine.toString());
            if (!parts.isEmpty()) {
                String key = parts.get(0);
                List<String> values = parts.subList(1, parts.size());
                properties.put(key, values);
            }
            currentLine.setLength(0);
        }
        reader.close();
    }

    private List<String> parseLine(String input) {
        List<String> parts = new ArrayList<>();
        int i = 0;
        while (i < input.length()) {
            if (input.charAt(i) == '"') {
                int j = input.indexOf('"', i + 1);
                if (j == -1) break;
                parts.add(input.substring(i + 1, j));
                i = j + 1;
            } else {
                int j = i;
                while (j < input.length() && !Character.isWhitespace(input.charAt(j))) j++;
                parts.add(input.substring(i, j));
                i = j;
            }
            while (i < input.length() && Character.isWhitespace(input.charAt(i))) i++;
        }
        return parts;
    }

    public void printProperties() {
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        System.out.println(gson.toJson(properties));
    }

    public void write(String filename) throws IOException {
        BufferedWriter writer = new BufferedWriter(new FileWriter(filename));
        for (Map.Entry<String, List<String>> entry : properties.entrySet()) {
            StringBuilder sb = new StringBuilder(entry.getKey());
            for (String value : entry.getValue()) {
                sb.append(value.contains(" ") ? " \"" + value + "\"" : " " + value);
            }
            writer.write(sb.toString());
            writer.newLine();
        }
        writer.close();
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     RobFile rob = new RobFile();
    //     rob.load("example.rob");
    //     rob.printProperties();
    //     rob.write("output.rob");
    // }
}

6. Javascript class that can open any file of format .ROB and decode read and write and print to console all the properties from the above list

const fs = require('fs'); // For Node.js environment

class RobFile {
  constructor() {
    this.properties = {};
  }

  load(filename) {
    const text = fs.readFileSync(filename, 'utf8');
    const lines = text.split('\n');
    let currentLine = '';
    for (let line of lines) {
      line = line.trim();
      if (line.startsWith('#') || line === '') continue;
      if (line.endsWith('\\')) {
        currentLine += line.slice(0, -1).trim() + ' ';
        continue;
      }
      currentLine += line;
      const parts = currentLine.match(/"[^"]*"|\S+/g) || [];
      const cleanedParts = parts.map(p => p.replace(/"/g, ''));
      if (cleanedParts.length > 0) {
        const key = cleanedParts[0];
        const values = cleanedParts.slice(1);
        this.properties[key] = values;
      }
      currentLine = '';
    }
  }

  printProperties() {
    console.log(JSON.stringify(this.properties, null, 2));
  }

  write(filename) {
    let content = '';
    for (const [key, values] of Object.entries(this.properties)) {
      let line = key;
      for (const value of values) {
        line += value.includes(' ') ? ` "${value}"` : ` ${value}`;
      }
      content += line + '\n';
    }
    fs.writeFileSync(filename, content);
  }
}

// Example usage:
// const rob = new RobFile();
// rob.load('example.rob');
// rob.printProperties();
// rob.write('output.rob');

7. Write a c class that can open any file of format .ROB and decode read and write and print to console all the properties from the above list

Since C does not have built-in classes like object-oriented languages, the following implements a struct RobFile with associated functions for loading, printing, and writing. It uses dynamic memory for flexibility. Compile with a C compiler (e.g., gcc) and link if needed.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_LINE 4096
#define MAX_KEYS 100
#define MAX_VALUES 1000

typedef struct {
    char *key;
    char **values;
    int value_count;
} Property;

typedef struct {
    Property properties[MAX_KEYS];
    int prop_count;
} RobFile;

void init_rob_file(RobFile *rob) {
    rob->prop_count = 0;
}

void free_rob_file(RobFile *rob) {
    for (int i = 0; i < rob->prop_count; i++) {
        free(rob->properties[i].key);
        for (int j = 0; j < rob->properties[i].value_count; j++) {
            free(rob->properties[i].values[j]);
        }
        free(rob->properties[i].values);
    }
}

int load_rob_file(RobFile *rob, const char *filename) {
    FILE *fp = fopen(filename, "r");
    if (!fp) return -1;
    char line[MAX_LINE];
    char current_line[MAX_LINE] = {0};
    while (fgets(line, MAX_LINE, fp)) {
        char *trimmed = line;
        while (isspace(*trimmed)) trimmed++;
        if (*trimmed == '#' || *trimmed == '\0') continue;
        size_t len = strlen(trimmed);
        if (trimmed[len - 1] == '\n') trimmed[--len] = '\0';
        if (trimmed[len - 1] == '\\') {
            trimmed[len - 1] = ' ';
            strcat(current_line, trimmed);
            continue;
        }
        strcat(current_line, trimmed);
        // Parse parts
        char *parts[MAX_VALUES];
        int part_count = 0;
        char *p = current_line;
        while (*p) {
            while (isspace(*p)) p++;
            if (!*p) break;
            if (*p == '"') {
                p++;
                char *start = p;
                while (*p && *p != '"') p++;
                if (*p) p++;
                int plen = p - start - 1;
                parts[part_count] = malloc(plen + 1);
                strncpy(parts[part_count++], start, plen);
                parts[part_count - 1][plen] = '\0';
            } else {
                char *start = p;
                while (*p && !isspace(*p)) p++;
                int plen = p - start;
                parts[part_count] = malloc(plen + 1);
                strncpy(parts[part_count++], start, plen);
                parts[part_count - 1][plen] = '\0';
            }
        }
        if (part_count > 0) {
            Property *prop = &rob->properties[rob->prop_count++];
            prop->key = parts[0];
            prop->values = malloc((part_count - 1) * sizeof(char*));
            prop->value_count = part_count - 1;
            for (int i = 1; i < part_count; i++) {
                prop->values[i - 1] = parts[i];
            }
        }
        current_line[0] = '\0';
    }
    fclose(fp);
    return 0;
}

void print_rob_properties(const RobFile *rob) {
    printf("{\n");
    for (int i = 0; i < rob->prop_count; i++) {
        const Property *prop = &rob->properties[i];
        printf("  \"%s\": [", prop->key);
        for (int j = 0; j < prop->value_count; j++) {
            printf("\"%s\"", prop->values[j]);
            if (j < prop->value_count - 1) printf(", ");
        }
        printf("]");
        if (i < rob->prop_count - 1) printf(",\n");
        else printf("\n");
    }
    printf("}\n");
}

int write_rob_file(const RobFile *rob, const char *filename) {
    FILE *fp = fopen(filename, "w");
    if (!fp) return -1;
    for (int i = 0; i < rob->prop_count; i++) {
        const Property *prop = &rob->properties[i];
        fprintf(fp, "%s", prop->key);
        for (int j = 0; j < prop->value_count; j++) {
            if (strchr(prop->values[j], ' ')) {
                fprintf(fp, " \"%s\"", prop->values[j]);
            } else {
                fprintf(fp, " %s", prop->values[j]);
            }
        }
        fprintf(fp, "\n");
    }
    fclose(fp);
    return 0;
}

// Example usage:
// int main() {
//     RobFile rob;
//     init_rob_file(&rob);
//     if (load_rob_file(&rob, "example.rob") == 0) {
//         print_rob_properties(&rob);
//         write_rob_file(&rob, "output.rob");
//     }
//     free_rob_file(&rob);
//     return 0;
// }