@foxglove/rosmsg
Advanced tools
Comparing version 4.2.2 to 5.0.1
export * from "./md5"; | ||
export * from "./parse"; | ||
export * from "./parseRos2idl"; | ||
export * from "./stringify"; | ||
//# sourceMappingURL=index.d.ts.map |
import { MessageDefinition } from "@foxglove/message-definition"; | ||
import { Grammar } from "nearley"; | ||
export declare const ROS2IDL_GRAMMAR: Grammar; | ||
export declare type ParseOptions = { | ||
@@ -14,4 +12,8 @@ /** Parse message definitions as ROS 2. Otherwise, parse as ROS1 */ | ||
export declare function parse(messageDefinition: string, options?: ParseOptions): MessageDefinition[]; | ||
/** | ||
* Normalize type names of complex types to fully qualified type names. | ||
* Example: `Marker` (defined in `visualization_msgs/MarkerArray` message) becomes `visualization_msgs/Marker`. | ||
*/ | ||
export declare function fixupTypes(types: MessageDefinition[]): void; | ||
export declare function normalizeType(type: string): string; | ||
//# sourceMappingURL=parse.d.ts.map |
{ | ||
"name": "@foxglove/rosmsg", | ||
"version": "4.2.2", | ||
"description": "Parser for ROS .msg and .idl definitions", | ||
"version": "5.0.1", | ||
"description": "Parser for ROS and ROS 2 .msg definitions", | ||
"license": "MIT", | ||
@@ -19,3 +19,2 @@ "repository": { | ||
"msgdef", | ||
"idl", | ||
"parser", | ||
@@ -22,0 +21,0 @@ "grammar" |
157
README.md
@@ -11,15 +11,15 @@ # @foxglove/rosmsg | ||
This library supports both [ROS1](http://wiki.ros.org/msg), [ROS 2](https://docs.ros.org/en/galactic/Concepts/About-ROS-Interfaces.html), and the [ROS 2 IDL subset](https://design.ros2.org/articles/idl_interface_definition.html)message definitions. | ||
This library supports both [ROS1](http://wiki.ros.org/msg) and [ROS 2](https://docs.ros.org/en/galactic/Concepts/About-ROS-Interfaces.html) message definitions. | ||
## Usage | ||
## ROS 1 Definition Usage | ||
```Typescript | ||
import { parse, parseRos2idl, stringify } from "@foxglove/rosmsg"; | ||
import { parse, stringify } from "@foxglove/rosmsg"; | ||
const definitionStr = `# geometry_msgs/msg/Pose | ||
geometry_msgs/msg/Point position | ||
geometry_msgs/msg/Quaternion orientation | ||
const definitionStr = `# geometry_msgs/Pose | ||
geometry_msgs/Point position | ||
geometry_msgs/Quaternion orientation | ||
=== | ||
MSG: geometry_msgs/msg/Point | ||
MSG: geometry_msgs/Point | ||
float64 x | ||
@@ -30,3 +30,3 @@ float64 y | ||
=== | ||
MSG: geometry_msgs/msg/Quaternion | ||
MSG: geometry_msgs/Quaternion | ||
float64 x | ||
@@ -38,51 +38,108 @@ float64 y | ||
const messageDefinition = parse(definitionStr); | ||
const messageDefinition = parse(definitionStr, {ros2: true}); // for ROS 2 definitions | ||
// stringify(messageDefinition) will return a canonical string, similar to | ||
// _definitionStr_ | ||
// print the parsed message definition structure | ||
console.log(JSON.stringify(messageDefinition, null, 2)); | ||
``` | ||
// ROS2IDL equivalent example | ||
const ros2idlDefinitionStr = ` | ||
================================================================================ | ||
IDL: geometry_msgs/msg/Pose | ||
Prints: | ||
module geometry_msgs { | ||
module msg { | ||
struct Pose { | ||
geometry_msgs::msg::Point position; | ||
geometry_msgs::msg::Quaternion orientation; | ||
}; | ||
}; | ||
}; | ||
```JSON | ||
[ | ||
{ | ||
"definitions": [ | ||
{ | ||
"type": "geometry_msgs/Point", | ||
"isArray": false, | ||
"name": "position", | ||
"isComplex": true | ||
}, | ||
{ | ||
"type": "geometry_msgs/Quaternion", | ||
"isArray": false, | ||
"name": "orientation", | ||
"isComplex": true | ||
} | ||
] | ||
}, | ||
{ | ||
"name": "geometry_msgs/Point", | ||
"definitions": [ | ||
{ | ||
"type": "float64", | ||
"isArray": false, | ||
"name": "x", | ||
"isComplex": false | ||
}, | ||
{ | ||
"type": "float64", | ||
"isArray": false, | ||
"name": "y", | ||
"isComplex": false | ||
}, | ||
{ | ||
"type": "float64", | ||
"isArray": false, | ||
"name": "z", | ||
"isComplex": false | ||
} | ||
] | ||
}, | ||
{ | ||
"name": "geometry_msgs/Quaternion", | ||
"definitions": [ | ||
{ | ||
"type": "float64", | ||
"isArray": false, | ||
"name": "x", | ||
"isComplex": false | ||
}, | ||
{ | ||
"type": "float64", | ||
"isArray": false, | ||
"name": "y", | ||
"isComplex": false | ||
}, | ||
{ | ||
"type": "float64", | ||
"isArray": false, | ||
"name": "z", | ||
"isComplex": false | ||
}, | ||
{ | ||
"type": "float64", | ||
"isArray": false, | ||
"name": "w", | ||
"isComplex": false | ||
} | ||
] | ||
} | ||
] | ||
``` | ||
================================================================================ | ||
IDL: geometry_msgs/msg/Point | ||
## ROS 2 Definition Usage | ||
module geometry_msgs { | ||
module msg { | ||
struct Point { | ||
double x; | ||
double y; | ||
double z; | ||
}; | ||
}; | ||
}; | ||
```Typescript | ||
import { parse, stringify } from "@foxglove/rosmsg"; | ||
================================================================================ | ||
IDL: geometry_msgs/msg/Quaternion | ||
const definitionStr = `# geometry_msgs/msg/Pose | ||
geometry_msgs/msg/Point position | ||
geometry_msgs/msg/Quaternion orientation | ||
module geometry_msgs { | ||
module msg { | ||
struct Quaternion { | ||
double x; | ||
double y; | ||
double z; | ||
double w; | ||
}; | ||
}; | ||
}; | ||
`; | ||
=== | ||
MSG: geometry_msgs/msg/Point | ||
float64 x | ||
float64 y | ||
float64 z | ||
const messageDefinition = parseRos2idl(ros2idlDefinitionStr); | ||
=== | ||
MSG: geometry_msgs/msg/Quaternion | ||
float64 x | ||
float64 y | ||
float64 z | ||
float64 w`; | ||
const messageDefinition = parse(definitionStr, {ros2: true}); | ||
// stringify(messageDefinition) will return a canonical string, similar to | ||
// _definitionStr_ | ||
// print the parsed message definition structure | ||
@@ -167,2 +224,6 @@ console.log(JSON.stringify(messageDefinition, null, 2)); | ||
## ROS 2 IDL Support | ||
See (`@foxglove/ros2idl-parser`)[https://github.com/foxglove/omgidl/packages/ros2idl-parser] for our implementation of `ros2idl` schema support. | ||
## License | ||
@@ -169,0 +230,0 @@ |
@@ -5,3 +5,2 @@ /// <reference types="./extensions" /> | ||
export * from "./parse"; | ||
export * from "./parseRos2idl"; | ||
export * from "./stringify"; |
@@ -493,2 +493,68 @@ // This file incorporates work covered by the following copyright and | ||
}); | ||
it("does not mixup types with same name but different namespace", () => { | ||
const messageDefinition = ` | ||
MSG: visualization_msgs/Marker | ||
int32 a | ||
=== | ||
MSG: aruco_msgs/Marker | ||
int32 b | ||
=== | ||
MSG: visualization_msgs/MarkerArray | ||
Marker[] a | ||
=== | ||
MSG: aruco_msgs/MarkerArray | ||
Marker[] b | ||
`; | ||
const types = parse(messageDefinition); | ||
expect(types).toEqual([ | ||
{ | ||
name: "visualization_msgs/Marker", | ||
definitions: [ | ||
{ | ||
type: "int32", | ||
isArray: false, | ||
name: "a", | ||
isComplex: false, | ||
}, | ||
], | ||
}, | ||
{ | ||
name: "aruco_msgs/Marker", | ||
definitions: [ | ||
{ | ||
type: "int32", | ||
isArray: false, | ||
name: "b", | ||
isComplex: false, | ||
}, | ||
], | ||
}, | ||
{ | ||
name: "visualization_msgs/MarkerArray", | ||
definitions: [ | ||
{ | ||
type: "visualization_msgs/Marker", | ||
isArray: true, | ||
name: "a", | ||
isComplex: true, | ||
}, | ||
], | ||
}, | ||
{ | ||
name: "aruco_msgs/MarkerArray", | ||
definitions: [ | ||
{ | ||
type: "aruco_msgs/Marker", | ||
isArray: true, | ||
name: "b", | ||
isComplex: true, | ||
}, | ||
], | ||
}, | ||
]); | ||
}); | ||
}); |
@@ -749,3 +749,3 @@ // This file incorporates work covered by the following copyright and | ||
## Severity level constants | ||
## | ||
## | ||
## These logging levels follow the Python Standard | ||
@@ -1047,2 +1047,68 @@ ## https://docs.python.org/3/library/logging.html#logging-levels | ||
}); | ||
it("does not mixup types with same name but different namespace", () => { | ||
const messageDefinition = ` | ||
MSG: visualization_msgs/msg/Marker | ||
int32 a | ||
=== | ||
MSG: aruco_msgs/msg/Marker | ||
int32 b | ||
=== | ||
MSG: visualization_msgs/msg/MarkerArray | ||
Marker[] a | ||
=== | ||
MSG: aruco_msgs/msg/MarkerArray | ||
Marker[] b | ||
`; | ||
const types = parse(messageDefinition, { ros2: true }); | ||
expect(types).toEqual([ | ||
{ | ||
name: "visualization_msgs/msg/Marker", | ||
definitions: [ | ||
{ | ||
type: "int32", | ||
isArray: false, | ||
name: "a", | ||
isComplex: false, | ||
}, | ||
], | ||
}, | ||
{ | ||
name: "aruco_msgs/msg/Marker", | ||
definitions: [ | ||
{ | ||
type: "int32", | ||
isArray: false, | ||
name: "b", | ||
isComplex: false, | ||
}, | ||
], | ||
}, | ||
{ | ||
name: "visualization_msgs/msg/MarkerArray", | ||
definitions: [ | ||
{ | ||
type: "visualization_msgs/msg/Marker", | ||
isArray: true, | ||
name: "a", | ||
isComplex: true, | ||
}, | ||
], | ||
}, | ||
{ | ||
name: "aruco_msgs/msg/MarkerArray", | ||
definitions: [ | ||
{ | ||
type: "aruco_msgs/msg/Marker", | ||
isArray: true, | ||
name: "b", | ||
isComplex: true, | ||
}, | ||
], | ||
}, | ||
]); | ||
}); | ||
}); |
@@ -15,6 +15,4 @@ // This file incorporates work covered by the following copyright and | ||
import ros1Rules from "./ros1.ne"; | ||
import ros2idlRules from "./ros2idl.ne"; | ||
const ROS1_GRAMMAR = Grammar.fromCompiled(ros1Rules); | ||
export const ROS2IDL_GRAMMAR = Grammar.fromCompiled(ros2idlRules); | ||
@@ -90,7 +88,14 @@ export type ParseOptions = { | ||
/** | ||
* Normalize type names of complex types to fully qualified type names. | ||
* Example: `Marker` (defined in `visualization_msgs/MarkerArray` message) becomes `visualization_msgs/Marker`. | ||
*/ | ||
export function fixupTypes(types: MessageDefinition[]): void { | ||
types.forEach(({ definitions }) => { | ||
types.forEach(({ definitions, name }) => { | ||
definitions.forEach((definition) => { | ||
if (definition.isComplex === true) { | ||
const foundName = findTypeByName(types, definition.type).name; | ||
// The type might be under a namespace (e.g. std_msgs or std_msgs/msg) which is required | ||
// to uniquely retrieve the type by its name. | ||
const typeNamespace = name?.split("/").slice(0, -1).join("/"); | ||
const foundName = findTypeByName(types, definition.type, typeNamespace).name; | ||
if (foundName == undefined) { | ||
@@ -139,3 +144,7 @@ throw new Error(`Missing type definition for ${definition.type}`); | ||
function findTypeByName(types: MessageDefinition[], name: string): MessageDefinition { | ||
function findTypeByName( | ||
types: MessageDefinition[], | ||
name: string, | ||
typeNamespace?: string, | ||
): MessageDefinition { | ||
const matches = types.filter((type) => { | ||
@@ -147,6 +156,13 @@ const typeName = type.name ?? ""; | ||
} | ||
// return if the search is in the type name | ||
// or matches exactly if a fully-qualified name match is passed to us | ||
const nameEnd = name.includes("/") ? name : `/${name}`; | ||
return typeName.endsWith(nameEnd); | ||
if (name.includes("/")) { | ||
// Fully-qualified name, match exact | ||
return typeName === name; | ||
} else if (typeNamespace) { | ||
// Type namespace is given, create fully-qualified name and match exact | ||
return typeName === `${typeNamespace}/${name}`; | ||
} else { | ||
// Fallback, return if the search is in the type name | ||
return typeName.endsWith(`/${name}`); | ||
} | ||
}); | ||
@@ -158,2 +174,6 @@ if (matches[0] == undefined) { | ||
} | ||
if (matches.length > 1) { | ||
throw new Error(`Cannot unambiguously determine fully-qualified type name for '${name}'`); | ||
} | ||
return matches[0]; | ||
@@ -160,0 +180,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
238
285022
30
4490