diff --git a/test_against_schema.py b/test_against_schema.py new file mode 100644 index 0000000..67299b9 --- /dev/null +++ b/test_against_schema.py @@ -0,0 +1,111 @@ +import json +import re +import os +import sys +import vda5050_msgs.msg as vda + + +def dromedary(non_dromedary_string) -> str: + x = camely(non_dromedary_string) + return x[0].lower() + x[1:] + + +def camely(non_camel_string) -> str: + return "".join(word[0].upper() + word[1:] for word in non_camel_string.split("_")) + + +def snakey(non_snake_string) -> str: + pattern = re.compile(r"(? str: + if ros_type == "uint32": + return "integer" + if ros_type == "double": + return "number" + if ros_type.startswith("sequence"): + return "array" + if ros_type.startswith("vda5050_msgs"): + return "object" + return ros_type + + +def inferType(schema_type): + try: + return getattr(vda, camely(schema_type)) + except AttributeError: + if schema_type == "information": + return vda.Info + raise KeyError(f"Could not infer type for {schema_type}") + + +def checkSchemaAgainstType(schema, type, global_definitions): + for field, data_type in type.get_fields_and_field_types().items(): + dromedary_field = dromedary(field) + field_description = f"'{dromedary_field}' of ROS message '{type.__name__}'" + if dromedary_field not in schema: + raise KeyError(f"{field_description} not in schema") + schema_type = schema[dromedary_field]["type"] + if isinstance(schema_type, list): + print(f"Skip type checking for '{field_description}' as it is a variant ") + continue + if schema_type != convert_type(data_type): + raise TypeError( + f"{field_description} should have type {schema_type} but has type {convert_type(data_type)}" + ) + for field, data in schema.items(): + if snakey(field) not in type.get_fields_and_field_types(): + raise KeyError( + f"Field '{snakey(field)}' not in ROS message '{type.__name__}'" + ) + if data["type"] == "array": + if "$ref" in data["items"]: + key = data["items"]["$ref"].split("/")[-1] + checkSchemaAgainstType( + global_definitions[key]["properties"], + inferType(key), + global_definitions, + ) + else: + # if it has a title use title otherwise infer it from field name (removing the plural s) + type_guess = data["items"].get("title", field.rstrip("s")) + checkSchemaAgainstType( + data["items"]["properties"], + inferType(type_guess), + global_definitions, + ) + elif data["type"] == "object": + for sub_field, sub_data in data["properties"].items(): + if sub_data["type"] == "object": + checkSchemaAgainstType( + sub_data, inferType(sub_data["type"]), global_definitions + ) + elif sub_data["type"] == "array": + if sub_data["items"]["type"] == "object": + type_guess = sub_data["items"].get( + "title", sub_field.rstrip("s") + ) + checkSchemaAgainstType( + sub_data["items"]["properties"], + inferType(type_guess), + global_definitions, + ) + + +def checkSchemaFile(schema_file, type): + with open(schema_file) as f: + schema = json.load(f) + checkSchemaAgainstType(schema["properties"], type, schema.get("definitions", {})) + + +if __name__ == "__main__": + folder = sys.argv[1] + for schema_file in os.listdir(folder): + if schema_file == "factsheet.schema": + print("Skip Checking Factsheet for now") + continue + checkSchemaFile( + os.path.join(folder, schema_file), + getattr(vda, camely(os.path.splitext(schema_file)[0])), + ) diff --git a/vda5050_msgs/CMakeLists.txt b/vda5050_msgs/CMakeLists.txt index 94fe3c6..352529f 100644 --- a/vda5050_msgs/CMakeLists.txt +++ b/vda5050_msgs/CMakeLists.txt @@ -24,16 +24,18 @@ set(msg_files "msg/BoundingBoxReference.msg" "msg/Connection.msg" "msg/ControlPoint.msg" + "msg/Corridor.msg" "msg/Edge.msg" "msg/EdgeState.msg" "msg/Error.msg" "msg/ErrorReference.msg" + "msg/Header.msg" "msg/Info.msg" "msg/InfoReference.msg" - "msg/Header.msg" "msg/InstantActions.msg" "msg/Load.msg" "msg/LoadDimensions.msg" + "msg/Map.msg" "msg/Node.msg" "msg/NodePosition.msg" "msg/NodeState.msg" @@ -46,7 +48,7 @@ set(msg_files ) rosidl_generate_interfaces(${PROJECT_NAME} ${msg_files} - DEPENDENCIES + DEPENDENCIES std_msgs builtin_interfaces ) diff --git a/vda5050_msgs/msg/ControlPoint.msg b/vda5050_msgs/msg/ControlPoint.msg index ba0de85..a78d5ab 100644 --- a/vda5050_msgs/msg/ControlPoint.msg +++ b/vda5050_msgs/msg/ControlPoint.msg @@ -2,9 +2,5 @@ float64 x # X coordinate described in the world coordinate system. float64 y # Y coordinate described in the world coordinate system. -float64 orientation # [rad] Range [-pi...pi] Orientation of the AGV on this position of the curve. - # The orientation is in world coordinates. - # When not defined the orientation of the AGV will be tangential to the curve. - float64 weight # Range [0..infinity) The weight with which this control point pulls on the curve. # When not defined, the default will be 1.0 diff --git a/vda5050_msgs/msg/Corridor.msg b/vda5050_msgs/msg/Corridor.msg new file mode 100644 index 0000000..b2e1945 --- /dev/null +++ b/vda5050_msgs/msg/Corridor.msg @@ -0,0 +1,7 @@ +float64 left_width # Defines the width of the corridor in meters to the left related to the trajectory of the vehicle. +float64 right_width # Defines the width of the corridor in meters to the right related to the trajectory of the vehicle. +string corridor_ref_point # Defines whether the boundaries are valid for the kinematic center or the contour of the vehicle. + +#Enums for corridor_ref_point +string KINEMATICCENTER="KINEMATICCENTER" +string CONTOUR="CONTOUR" diff --git a/vda5050_msgs/msg/Edge.msg b/vda5050_msgs/msg/Edge.msg index e1edd05..7b33ca6 100644 --- a/vda5050_msgs/msg/Edge.msg +++ b/vda5050_msgs/msg/Edge.msg @@ -29,6 +29,8 @@ float64 orientation # [rad] Orientation of the AGV on the edge r # If a trajectory with orientation is defined, follow the trajectories orientation. # If a trajectory without orientation and the orientation field here is defined, # apply the orientation to the tangent of the trajectory. +string orientation_type # "GLOBAL"- relative to the global project specific map coordinate system; + # "TANGENTIAL"- tangential to the edge. If not defined, the default value is "TANGENTIAL" string direction # Sets direction at junctions for line-guided vehicles, to be defined initially # (vehicle individual) Example: left, right, straight, 433MHz @@ -39,6 +41,7 @@ float64 max_rotation_speed # [rad/s] Maximum rotation speed Optional: N vda5050_msgs/Trajectory trajectory # Trajectory JSON-object for this edge as a NURBS. Defines the curve on which the # AGV should move between start_node and end_node. Optional: Can be omitted if AGV # cannot process trajectories or if AGV plans its own trajectory. +vda5050_msgs/Corridor corridor # Definition of boundaries in which a vehicle can deviate from its trajectory, e. g. to avoid obstacles. float64 length # [m] Length of the path from startNode to endNode. Optional: This value is used # by lineguided AGVs to decrease their speed before reaching a stop position. @@ -46,3 +49,7 @@ vda5050_msgs/Action[] actions # Array of action_ids to be executed on the # only be active for the time that the AGV is traversing the edge which triggered # the action. When the AGV leaves the edge, the action will stop and the state # before entering the edge will be restored. + +# Enum for orientation_type +string GLOBAL="GLOBAL" +string TANGENTIAL="TANGENTIAL" diff --git a/vda5050_msgs/msg/Error.msg b/vda5050_msgs/msg/Error.msg index fddbc7c..0254209 100644 --- a/vda5050_msgs/msg/Error.msg +++ b/vda5050_msgs/msg/Error.msg @@ -6,7 +6,8 @@ string error_description # Error description string error_level # Enum {warning, fatal} warning: AGV is ready to start (e.g. maintenance # cycle expiration warning) fatal: AGV is not in running condition, user # intervention required (e.g. laser scanner is contaminated) +string error_hint # Hint on how to approach or solve the reported error. # Enums for error_level -string WARNING="warning" -string FATAL="fatal" +string WARNING="WARNING" +string FATAL="FATAL" diff --git a/vda5050_msgs/msg/InstantActions.msg b/vda5050_msgs/msg/InstantActions.msg index 474e6c7..75fe8de 100644 --- a/vda5050_msgs/msg/InstantActions.msg +++ b/vda5050_msgs/msg/InstantActions.msg @@ -9,4 +9,4 @@ string manufacturer # Manufacturer of the AGV string serial_number # Serial Number of the AGV -Action[] instant_actions # List of actions to execute +Action[] actions # List of actions to execute diff --git a/vda5050_msgs/msg/Load.msg b/vda5050_msgs/msg/Load.msg index 0723cf5..6d02d26 100644 --- a/vda5050_msgs/msg/Load.msg +++ b/vda5050_msgs/msg/Load.msg @@ -14,4 +14,4 @@ vda5050_msgs/BoundingBoxReference bounding_box_reference # Point of reference f vda5050_msgs/LoadDimensions load_dimensions # Dimensions of the load’s bounding box in meters. -uint32 weight # Absolute weight of the load measured in kg. +float64 weight # Absolute weight of the load measured in kg. diff --git a/vda5050_msgs/msg/Map.msg b/vda5050_msgs/msg/Map.msg new file mode 100644 index 0000000..2296ccb --- /dev/null +++ b/vda5050_msgs/msg/Map.msg @@ -0,0 +1,8 @@ +string map_id # ID of the map describing a defined area of the vehicle's workspace. +string map_version # Version of the map. +string map_description # Additional information on the map. +string map_status # Information on the status of the map indicating, if a map version is currently used on the vehicle. ENABLED: Indicates this map is currently active / used on the AGV. At most one map with the same mapId can have its status set to ENABLED.
DISABLED: Indicates this map version is currently not enabled on the AGV and thus could be enabled or deleted by request. + +# Enums for map status +string ENABLED="ENABLED" +string DISABLED="DISABLED" diff --git a/vda5050_msgs/msg/State.msg b/vda5050_msgs/msg/State.msg index 2682372..6b07e81 100644 --- a/vda5050_msgs/msg/State.msg +++ b/vda5050_msgs/msg/State.msg @@ -51,10 +51,6 @@ vda5050_msgs/AGVPosition agv_position # Current position of the A vda5050_msgs/Velocity velocity # AGV's velocity in vehicle coordinates -vda5050_msgs/Trajectory trajectory # The trajectory is to be communicated as a NURBS and is defined in - # chapter 5.1. Optional: AGVs that plan their own path are to - # communicate their path via a trajectory object. - vda5050_msgs/Load[] loads # Loads that are currently handled by the AGV. # Optional: If AGV cannot determine load state, leave the array out of the state. # If the AGV can determine the load state, but the array is empty, the AGV is considered unloaded. @@ -72,11 +68,10 @@ vda5050_msgs/Error[] errors # Array of errorobjects. Em vda5050_msgs/SafetyState safety_state # Contains all safetyrelated information. -vda5050_msgs/Info[] informations # Array of info-objects. An empty array indicates that the AGV has no information. - # This should only be used for visualization or debugging – it must not be used for logic in master control - # Changed to information with 2.0 - - +vda5050_msgs/Info[] information # Array of info-objects. An empty array indicates that the AGV has no information. + # This should only be used for visualization or debugging – it must not be used for logic in master control + # Changed to information with 2.0 +vda5050_msgs/Map[] maps #Array of map-objects that are currently stored on the vehicle. # Enums for operatingMode string AUTOMATIC="AUTOMATIC"