mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-07-27 00:48:23 +08:00
Pyuavcan and DSDL removed, will be returned back as submodules in the next commit
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
Standard DSDL definitions
|
||||
=========================
|
||||
|
||||
For details, please refer to the [UAVCAN specification](http://uavcan.org/).
|
||||
|
||||
## Standard DTID ranges
|
||||
|
||||
- Messages: [256, 1791)
|
||||
- Services: [64, 447)
|
||||
|
||||
Rest are reserved for vendor-specific data types.
|
||||
|
||||
## Standard ID grouping
|
||||
|
||||
Note that all unallocated space can be claimed later.
|
||||
|
||||
### Messages
|
||||
|
||||
| ID | Types | Note |
|
||||
| -------------------- | ---------------------------------------- | ---------------------------------------- |
|
||||
| [256, 260) | protocol.* | Highest priority |
|
||||
| [260, 900) | equipment.* | High priority |
|
||||
| [1000, 1050) | protocol.* | |
|
||||
| [1400, 1700) | equipment.* | Low priority |
|
||||
| 1780 | mavlink.Message | |
|
||||
| [1785, 1791) | protocol.debug.* | Lowest priority |
|
||||
|
||||
### Services
|
||||
|
||||
| ID | Types | Note |
|
||||
| -------------------- | ---------------------------------------- | ---------------------------------------- |
|
||||
| [100, 110) | equipment.* | |
|
||||
| [200, 250) | protocol.* | |
|
||||
@@ -1,7 +0,0 @@
|
||||
#
|
||||
# Global timestamp in hectomicroseconds (1 = 100 usec), 6 bytes.
|
||||
#
|
||||
|
||||
uint48 UNKNOWN = 0
|
||||
uint48 USEC_PER_LSB = 100 # Timestamp resolution
|
||||
truncated uint48 husec # Hectomicroseconds (10^-4)
|
||||
@@ -1,16 +0,0 @@
|
||||
#
|
||||
# Fast automatic self test request.
|
||||
# http://uavcan.org/Standard_data_types_and_application_level_functions#Pre-operational_checks
|
||||
#
|
||||
|
||||
int48 argument
|
||||
|
||||
---
|
||||
|
||||
# Amount of extra time the node needs to finish the procedure.
|
||||
uint16 delay_msec
|
||||
|
||||
# Status:
|
||||
# true - the requested procedure is being executed or was executed
|
||||
# false - the requested procedure can't be executed right now
|
||||
bool ok
|
||||
@@ -1,16 +0,0 @@
|
||||
#
|
||||
# Fast automatic calibration request.
|
||||
# http://uavcan.org/Standard_data_types_and_application_level_functions#Pre-operational_checks
|
||||
#
|
||||
|
||||
int48 argument
|
||||
|
||||
---
|
||||
|
||||
# Amount of extra time the node needs to finish the procedure.
|
||||
uint16 delay_msec
|
||||
|
||||
# Status:
|
||||
# true - the requested procedure is being executed or was executed
|
||||
# false - the requested procedure can't be executed right now
|
||||
bool ok
|
||||
@@ -1,20 +0,0 @@
|
||||
#
|
||||
# Nested type.
|
||||
# Coarse, low-resolution 3D orientation represented as fixed axes in 16 bit.
|
||||
#
|
||||
# Roll, pitch, yaw angles in radians should be multiplied by
|
||||
# ANGLE_MULTIPLIER in order to convert them to the coarse representation.
|
||||
#
|
||||
# ANGLE_MULTIPLIER = NORM / PI
|
||||
#
|
||||
# Where NORM is 12, because it:
|
||||
# - Fits the maximum range of a signed 5 bit integer
|
||||
# - Allows to exactly represent the following angles:
|
||||
# 0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, and negatives
|
||||
#
|
||||
|
||||
float32 ANGLE_MULTIPLIER = 4.7746482927568605
|
||||
|
||||
int5[3] fixed_axis_roll_pitch_yaw
|
||||
|
||||
bool orientation_defined # False if the orientation is actually not defined
|
||||
@@ -1,6 +0,0 @@
|
||||
#
|
||||
# Actuator commands.
|
||||
# The system supports up to 256 actuators; up to 31 of them can be commanded with one message.
|
||||
#
|
||||
|
||||
Command[<32] commands
|
||||
@@ -1,16 +0,0 @@
|
||||
#
|
||||
# Generic actuator feedback, if available.
|
||||
# Unknown fields should be set to NAN.
|
||||
#
|
||||
|
||||
uint8 actuator_id
|
||||
|
||||
#
|
||||
# Whether the units are linear or angular depends on the actuator type (refer to the Command data type).
|
||||
#
|
||||
float16 position # meter or radian
|
||||
float16 force # Newton or Newton metre
|
||||
float16 speed # meter per second or radian per second
|
||||
|
||||
uint7 POWER_RATING_PCT_UNKNOWN = 127
|
||||
uint7 power_rating_pct # 0 - unloaded, 100 - full load
|
||||
@@ -1,20 +0,0 @@
|
||||
#
|
||||
# Nested type.
|
||||
# Single actuator command.
|
||||
#
|
||||
|
||||
uint8 actuator_id
|
||||
|
||||
#
|
||||
# Whether the units are linear or angular depends on the actuator type.
|
||||
#
|
||||
uint4 COMMAND_TYPE_UNITLESS = 0 # [-1, 1]
|
||||
uint4 COMMAND_TYPE_POSITION = 1 # meter or radian
|
||||
uint4 COMMAND_TYPE_FORCE = 2 # Newton or Newton metre
|
||||
uint4 COMMAND_TYPE_SPEED = 3 # meter per second or radian per second
|
||||
uint4 command_type
|
||||
|
||||
#
|
||||
# Value of the above type
|
||||
#
|
||||
float16 command_value
|
||||
@@ -1,17 +0,0 @@
|
||||
#
|
||||
# Inertial data and orientation in body frame.
|
||||
#
|
||||
|
||||
uavcan.Timestamp timestamp
|
||||
|
||||
# Normalized quaternion
|
||||
float16[4] orientation_xyzw
|
||||
float16[<=9] orientation_covariance
|
||||
|
||||
# rad/sec
|
||||
float16[3] angular_velocity
|
||||
float16[<=9] angular_velocity_covariance
|
||||
|
||||
# m/s^2
|
||||
float16[3] linear_acceleration
|
||||
float16[<=9] linear_acceleration_covariance
|
||||
@@ -1,6 +0,0 @@
|
||||
#
|
||||
# Magnetic field readings in body frame.
|
||||
#
|
||||
|
||||
float16[3] magnetic_field
|
||||
float16[<=9] magnetic_field_covariance
|
||||
@@ -1,6 +0,0 @@
|
||||
#
|
||||
# TAS.
|
||||
#
|
||||
|
||||
float16 true_airspeed # m/s
|
||||
float16 true_airspeed_variance # (m/s)^2
|
||||
@@ -1,6 +0,0 @@
|
||||
#
|
||||
# IAS.
|
||||
#
|
||||
|
||||
float16 indicated_airspeed # m/s
|
||||
float16 indicated_airspeed_variance # (m/s)^2
|
||||
@@ -1,10 +0,0 @@
|
||||
#
|
||||
# Angle of attack.
|
||||
#
|
||||
|
||||
uint8 SENSOR_ID_LEFT = 254
|
||||
uint8 SENSOR_ID_RIGHT = 255
|
||||
uint8 sensor_id
|
||||
|
||||
float16 aoa # Radians
|
||||
float16 aoa_variance # Radians^2
|
||||
@@ -1,7 +0,0 @@
|
||||
#
|
||||
# Body sideslip in radians.
|
||||
# Yaw right: +, yaw left: -
|
||||
#
|
||||
|
||||
float16 sideslip_angle # Radians
|
||||
float16 sideslip_angle_variance # Radians^2
|
||||
@@ -1,6 +0,0 @@
|
||||
#
|
||||
# Static pressure.
|
||||
#
|
||||
|
||||
float32 static_pressure # Pascal
|
||||
float16 static_pressure_variance # Pascal^2
|
||||
@@ -1,6 +0,0 @@
|
||||
#
|
||||
# Static temperature.
|
||||
#
|
||||
|
||||
float16 static_temperature # Kelvin
|
||||
float16 static_temperature_variance # Kelvin^2
|
||||
@@ -1,22 +0,0 @@
|
||||
#
|
||||
# Generic camera gimbal control.
|
||||
#
|
||||
# This message can only be used in the following modes:
|
||||
# - COMMAND_MODE_ANGULAR_VELOCITY
|
||||
# - COMMAND_MODE_ORIENTATION_FIXED_FRAME
|
||||
# - COMMAND_MODE_ORIENTATION_BODY_FRAME
|
||||
#
|
||||
|
||||
uint8 gimbal_id
|
||||
|
||||
#
|
||||
# Target operation mode - how to handle this message.
|
||||
# See the list of acceptable modes above.
|
||||
#
|
||||
Mode mode
|
||||
|
||||
#
|
||||
# In the angular velocity mode, this field contains a rate quaternion.
|
||||
# In the orientation mode, this field contains orientation either in fixed frame or in body frame.
|
||||
#
|
||||
float16[4] quaternion_xyzw
|
||||
@@ -1,25 +0,0 @@
|
||||
#
|
||||
# Generic camera gimbal control.
|
||||
#
|
||||
# This message can only be used in the following modes:
|
||||
# - COMMAND_MODE_GEO_POI
|
||||
#
|
||||
|
||||
uint8 gimbal_id
|
||||
|
||||
#
|
||||
# Target operation mode - how to handle this message.
|
||||
# See the list of acceptable modes above.
|
||||
#
|
||||
Mode mode
|
||||
|
||||
#
|
||||
# Coordinates of the POI (point of interest).
|
||||
#
|
||||
int32 longitude_deg_1e7 # 1 LSB = 1e-7 deg
|
||||
int32 latitude_deg_1e7
|
||||
int22 height_cm # 1 LSB = 10 mm
|
||||
|
||||
uint2 HEIGHT_REFERENCE_ELLIPSOID = 0
|
||||
uint2 HEIGHT_REFERENCE_MEAN_SEA_LEVEL = 1
|
||||
uint2 height_reference
|
||||
@@ -1,14 +0,0 @@
|
||||
#
|
||||
# Generic gimbal status.
|
||||
#
|
||||
|
||||
uint8 gimbal_id
|
||||
|
||||
Mode mode
|
||||
|
||||
#
|
||||
# Camera axis orientation in body frame (not in fixed frame).
|
||||
# Please refer to the UAVCAN coordinate frame conventions.
|
||||
#
|
||||
float16[4] camera_orientation_in_body_frame_xyzw
|
||||
float16[<=9] camera_orientation_in_body_frame_covariance # +inf for non-existent axes
|
||||
@@ -1,9 +0,0 @@
|
||||
#
|
||||
# Gimbal operating mode
|
||||
#
|
||||
|
||||
uint8 COMMAND_MODE_ANGULAR_VELOCITY = 0
|
||||
uint8 COMMAND_MODE_ORIENTATION_FIXED_FRAME = 1
|
||||
uint8 COMMAND_MODE_ORIENTATION_BODY_FRAME = 2
|
||||
uint8 COMMAND_MODE_GEO_POI = 3
|
||||
uint8 command_mode
|
||||
@@ -1,7 +0,0 @@
|
||||
#
|
||||
# Raw ESC command.
|
||||
# Non-zero setpoint value below minimum should be interpreted as min valid setpoint for the given motor.
|
||||
# Negative values indicate reverse rotation.
|
||||
#
|
||||
|
||||
int14[<=20] cmd
|
||||
@@ -1,6 +0,0 @@
|
||||
#
|
||||
# Simple RPM setpoint.
|
||||
# Negative values indicate reverse rotation.
|
||||
#
|
||||
|
||||
int18[<=20] rpm
|
||||
@@ -1,16 +0,0 @@
|
||||
#
|
||||
# Generic ESC status.
|
||||
# Unknown fields should be set to NAN.
|
||||
#
|
||||
|
||||
uint32 error_count # Resets when the motor restarts
|
||||
|
||||
float16 voltage # Volt
|
||||
float16 current # Ampere. Can be negative in case of a regenerative braking.
|
||||
float16 temperature # Kelvin
|
||||
|
||||
int18 rpm # Negative value indicates reverse rotation
|
||||
|
||||
uint7 power_rating_pct # Percent of maximum power
|
||||
|
||||
uint5 esc_index
|
||||
@@ -1,52 +0,0 @@
|
||||
#
|
||||
# GNSS navigation solution with uncertainty.
|
||||
#
|
||||
|
||||
uavcan.Timestamp timestamp # Global network-synchronized time, if available, otherwise zero
|
||||
|
||||
#
|
||||
# Time solution
|
||||
#
|
||||
# The following rules apply:
|
||||
# - if num_leap_seconds = 0, it is assumed that its value is unknown, and that gnss_timestamp contains UTC time.
|
||||
# - if num_leap_seconds = 1, it is assumed that its value is unknown, and that gnss_timestamp contains GPS time.
|
||||
# - if num_leap_seconds = 2, it is assumed that its value is unknown, and that gnss_timestamp contains TAI time.
|
||||
# - if num_leap_seconds > 2, it is assumed that its value is KNOWN, and that gnss_timestamp contains UTC time.
|
||||
#
|
||||
# At the time of February 2015, the number of leap seconds is 26.
|
||||
#
|
||||
uavcan.Timestamp gnss_timestamp # GNSS timestamp, if available, otherwise zero
|
||||
|
||||
uint8 NUM_LEAP_SECONDS_UNKNOWN_GNSS_TIME_UTC = 0 # num_leap_seconds is unknown, gnss_timestamp contains UTC time
|
||||
uint8 NUM_LEAP_SECONDS_UNKNOWN_GNSS_TIME_GPS = 1 # num_leap_seconds is unknown, gnss_timestamp contains GPS time
|
||||
uint8 NUM_LEAP_SECONDS_UNKNOWN_GNSS_TIME_TAI = 2 # num_leap_seconds is unknown, gnss_timestamp contains TAI time
|
||||
uint8 num_leap_seconds
|
||||
|
||||
#
|
||||
# Position and velocity solution
|
||||
#
|
||||
int37 longitude_deg_1e8 # Longitude degrees multiplied by 1e8 (approx. 1 mm per LSB)
|
||||
int37 latitude_deg_1e8 # Latitude degrees multiplied by 1e8 (approx. 1 mm per LSB on equator)
|
||||
int27 height_ellipsoid_mm # Height above ellipsoid in millimeters
|
||||
int27 height_msl_mm # Height above mean sea level in millimeters
|
||||
|
||||
float16[3] ned_velocity # NED frame (north-east-down) in meters per second
|
||||
|
||||
#
|
||||
# Fix status
|
||||
#
|
||||
uint6 sats_used
|
||||
|
||||
uint2 STATUS_NO_FIX = 0
|
||||
uint2 STATUS_TIME_ONLY = 1
|
||||
uint2 STATUS_2D_FIX = 2
|
||||
uint2 STATUS_3D_FIX = 3
|
||||
uint2 status
|
||||
|
||||
#
|
||||
# Precision
|
||||
#
|
||||
float16 pdop
|
||||
|
||||
float16[<=9] position_covariance # m^2
|
||||
float16[<=9] velocity_covariance # (m/s)^2
|
||||
@@ -1,15 +0,0 @@
|
||||
#
|
||||
# GNSS low priority auxiliary info.
|
||||
# Unknown DOP parameters should be set to NAN.
|
||||
#
|
||||
|
||||
float16 gdop
|
||||
float16 pdop
|
||||
float16 hdop
|
||||
float16 vdop
|
||||
float16 tdop
|
||||
float16 ndop
|
||||
float16 edop
|
||||
|
||||
uint7 sats_visible # All visible sats of all available GNSS (e.g. GPS, GLONASS, etc)
|
||||
uint6 sats_used # All used sats of all available GNSS
|
||||
@@ -1,11 +0,0 @@
|
||||
#
|
||||
# GNSS RTCM SC-104 protocol raw stream container.
|
||||
# RTCM messages that are longer than max data size can be split over multiple consecutive messages.
|
||||
#
|
||||
|
||||
uint8 PROTOCOL_ID_UNKNOWN = 0
|
||||
uint8 PROTOCOL_ID_RTCM2 = 2
|
||||
uint8 PROTOCOL_ID_RTCM3 = 3
|
||||
uint8 protocol_id
|
||||
|
||||
uint8[<=100] data
|
||||
@@ -1,8 +0,0 @@
|
||||
#
|
||||
# Generic cargo holder/hardpoint command.
|
||||
#
|
||||
|
||||
uint8 hardpoint_id
|
||||
|
||||
# Either a binary command (0 - release, 1+ - hold) or bitmask
|
||||
uint16 command
|
||||
@@ -1,11 +0,0 @@
|
||||
#
|
||||
# Generic cargo holder/hardpoint status.
|
||||
#
|
||||
|
||||
uint8 hardpoint_id
|
||||
|
||||
float16 cargo_weight # Newton
|
||||
float16 cargo_weight_variance
|
||||
|
||||
# Meaning is the same as for the command field in the Command message
|
||||
uint16 status
|
||||
@@ -1,7 +0,0 @@
|
||||
#
|
||||
# Nodes that are capable of making noise (e.g. ESC) should obey.
|
||||
# Use case: pre-arm warning sound, error indication.
|
||||
#
|
||||
|
||||
float16 frequency # Hz
|
||||
float16 duration # Sec
|
||||
@@ -1,5 +0,0 @@
|
||||
#
|
||||
# Lights control command.
|
||||
#
|
||||
|
||||
SingleLightCommand[<32] commands
|
||||
@@ -1,9 +0,0 @@
|
||||
#
|
||||
# Nested type.
|
||||
# RGB color in the standard 5-6-5 16-bit palette.
|
||||
# Monocolor lights should interpret this as brightness setpoint: from zero (0, 0, 0) to full brightness (31, 63, 31).
|
||||
#
|
||||
|
||||
uint5 red
|
||||
uint6 green
|
||||
uint5 blue
|
||||
@@ -1,8 +0,0 @@
|
||||
#
|
||||
# Nested type.
|
||||
# Controls single light source, color or monochrome.
|
||||
#
|
||||
|
||||
uint8 light_id
|
||||
|
||||
RGB565 color # Monocolor lights should interpret this as brightness
|
||||
@@ -1,14 +0,0 @@
|
||||
#
|
||||
# Primary power supply status.
|
||||
# Typical publishing rate should be around 1~2 Hz.
|
||||
#
|
||||
|
||||
# How many hours left to full discharge at average load over the last 10 seconds
|
||||
float16 hours_to_empty_at_10sec_avg_power # [Hours]
|
||||
float16 hours_to_empty_at_10sec_avg_power_variance # [Hours^2]
|
||||
|
||||
uint7 remaining_energy_pct # [Percent] Required
|
||||
uint7 remaining_energy_pct_stdev # [Percent] Error standard deviation. Use best guess if unknown
|
||||
|
||||
# True if the publishing node senses that an external power source can be used, e.g. to charge batteries
|
||||
bool external_power_available
|
||||
@@ -1,14 +0,0 @@
|
||||
#
|
||||
# Generic electrical circuit info.
|
||||
#
|
||||
|
||||
uint16 circuit_id
|
||||
|
||||
float16 voltage
|
||||
float16 current
|
||||
|
||||
uint8 ERROR_MASK_OVERVOLTAGE = 1
|
||||
uint8 ERROR_MASK_UNDERVOLTAGE = 2
|
||||
uint8 ERROR_MASK_OVERCURRENT = 4
|
||||
uint8 ERROR_MASK_UNDERCURRENT = 8
|
||||
uint8 error_mask
|
||||
@@ -1,62 +0,0 @@
|
||||
#
|
||||
# Single battery info.
|
||||
# Typical publishing rate should be around 0.2~1 Hz.
|
||||
# Please refer to the Smart Battery data specification for some elaboration.
|
||||
#
|
||||
|
||||
#
|
||||
# Primary parameters.
|
||||
# Some fields can be set to NAN if their values are unknown.
|
||||
# Full charge capacity is expected to slowly reduce as the battery is aging. Normally its estimate is updated after
|
||||
# every charging cycle.
|
||||
#
|
||||
float16 temperature # [Kelvin]
|
||||
float16 voltage # [Volt]
|
||||
float16 current # [Ampere]
|
||||
float16 average_power_10sec # [Watt] Average power consumption over the last 10 seconds
|
||||
float16 remaining_capacity_wh # [Watt hours] Will be increasing during charging
|
||||
float16 full_charge_capacity_wh # [Watt hours] Predicted battery capacity when it is fully charged. Falls with aging
|
||||
float16 hours_to_full_charge # [Hours] Charging is expected to complete in this time; zero if not charging
|
||||
|
||||
#
|
||||
# Status mask.
|
||||
# Notes:
|
||||
# - CHARGING must be always set as long as the battery is connected to a charger, even if the charging is complete.
|
||||
# - CHARGED must be cleared immediately when the charger is disconnected.
|
||||
#
|
||||
uint11 STATUS_MASK_IN_USE = 1 # The battery is currently used as a power supply
|
||||
uint11 STATUS_MASK_CHARGING = 2 # Charger is active
|
||||
uint11 STATUS_MASK_CHARGED = 4 # Charging complete, but the charger is still active
|
||||
uint11 STATUS_MASK_TEMP_HOT = 8 # Battery temperature is above normal
|
||||
uint11 STATUS_MASK_TEMP_COLD = 16 # Battery temperature is below normal
|
||||
uint11 STATUS_MASK_OVERLOAD = 32 # Safe operating area violation
|
||||
uint11 STATUS_MASK_BAD_BATTERY = 64 # This battery should not be used anymore (e.g. low SOH)
|
||||
uint11 STATUS_MASK_NEED_SERVICE = 128 # This battery requires maintenance (e.g. balancing, full recharge)
|
||||
uint11 STATUS_MASK_BMS_ERROR = 256 # Battery management system/controller error, smart battery interface error
|
||||
uint11 STATUS_MASK_RESERVED_A = 512 # Keep zero
|
||||
uint11 STATUS_MASK_RESERVED_B = 1024 # Keep zero
|
||||
uint11 status_mask
|
||||
|
||||
#
|
||||
# State of Health (SOH) estimate, in percent.
|
||||
# http://en.wikipedia.org/wiki/State_of_health
|
||||
#
|
||||
uint7 STATE_OF_HEALTH_UNKNOWN = 127 # Use this constant if SOH cannot be estimated
|
||||
uint7 state_of_health_pct # Health of the battery, in percent, optional
|
||||
|
||||
#
|
||||
# Relative State of Charge (SOC) estimate, in percent.
|
||||
# http://en.wikipedia.org/wiki/State_of_charge
|
||||
#
|
||||
uint7 state_of_charge_pct # Percent of the full charge [0, 100]. This field is required
|
||||
uint7 state_of_charge_pct_stdev # SOC error standard deviation; use best guess if unknown
|
||||
|
||||
#
|
||||
# Battery identification.
|
||||
# Model instance ID must be unique within the same battery model name.
|
||||
# Model name is a human-readable string that normally should include the vendor name, model name, and chemistry
|
||||
# type of this battery. This field should be assumed case-insensitive. Example: "Zubax Smart Battery v1.1 LiPo".
|
||||
#
|
||||
uint8 battery_id # Identifies the battery within this vehicle, e.g. 0 - primary battery
|
||||
uint32 model_instance_id # Set to zero if not applicable
|
||||
uint8[<32] model_name # Battery model name
|
||||
@@ -1,25 +0,0 @@
|
||||
#
|
||||
# Generic narrow-beam range sensor data.
|
||||
#
|
||||
|
||||
uavcan.Timestamp timestamp
|
||||
|
||||
uint8 sensor_id
|
||||
|
||||
uavcan.equipment.CoarseOrientation beam_orientation # In body frame
|
||||
|
||||
float16 field_of_view # Radians
|
||||
|
||||
uint5 SENSOR_TYPE_UNDEFINED = 0
|
||||
uint5 SENSOR_TYPE_SONAR = 1
|
||||
uint5 SENSOR_TYPE_LIDAR = 2
|
||||
uint5 SENSOR_TYPE_RADAR = 3
|
||||
uint5 sensor_type
|
||||
|
||||
uint3 READING_TYPE_UNDEFINED = 0 # Range is unknown
|
||||
uint3 READING_TYPE_VALID_RANGE = 1 # Range field contains valid distance
|
||||
uint3 READING_TYPE_TOO_CLOSE = 2 # Range field contains min range for the sensor
|
||||
uint3 READING_TYPE_TOO_FAR = 3 # Range field contains max range for the sensor
|
||||
uint3 reading_type
|
||||
|
||||
float16 range # Meters
|
||||
@@ -1,9 +0,0 @@
|
||||
#
|
||||
# This message represents the system arming status.
|
||||
# Some nodes may refuse to operate unless the system is fully armed.
|
||||
#
|
||||
|
||||
uint8 STATUS_DISARMED = 0
|
||||
uint8 STATUS_FULLY_ARMED = 255
|
||||
|
||||
uint8 status
|
||||
@@ -1,10 +0,0 @@
|
||||
#
|
||||
# Encapsulated MAVLink message.
|
||||
#
|
||||
|
||||
uint8 seq
|
||||
uint8 sysid
|
||||
uint8 compid
|
||||
uint8 msgid
|
||||
|
||||
uint8[<=105] payload
|
||||
@@ -1,30 +0,0 @@
|
||||
#
|
||||
# Abstract node status information.
|
||||
# Any UAVCAN node is required to publish this message periodically.
|
||||
# It is NOT recommended to change its publication rate at run time.
|
||||
#
|
||||
# See http://uavcan.org/Standard_data_types_and_application_level_functions
|
||||
#
|
||||
|
||||
uint16 MAX_PUBLICATION_PERIOD_MS = 1000
|
||||
uint16 MIN_PUBLICATION_PERIOD_MS = 2
|
||||
|
||||
# If a node fails to publish this message in this amount of time, it should be considered offline.
|
||||
uint16 OFFLINE_TIMEOUT_MS = 3000
|
||||
|
||||
# Uptime counter should never overflow.
|
||||
uint28 uptime_sec
|
||||
|
||||
# Status code should be used to reflect the node status in the most abstract way.
|
||||
# OFFLINE status can be actually reported by the node to explicitly inform other network
|
||||
# participants that the sending node is about to shutdown. In this case other nodes will not
|
||||
# have to wait OFFLINE_TIMEOUT_MS before they detect that the node is no longer available.
|
||||
uint4 STATUS_OK = 0
|
||||
uint4 STATUS_INITIALIZING = 1
|
||||
uint4 STATUS_WARNING = 2
|
||||
uint4 STATUS_CRITICAL = 3
|
||||
uint4 STATUS_OFFLINE = 15
|
||||
uint4 status_code
|
||||
|
||||
# Optional, vendor-specific node status code, e.g. a fault code or a status bitmask.
|
||||
uint16 vendor_specific_status_code
|
||||
@@ -1,5 +0,0 @@
|
||||
#
|
||||
# This message can be broadcasted in order to perform global network discovery.
|
||||
# Alternatively, it is possible to wait some time so each node of the network
|
||||
# transmits NodeStatus message at least once.
|
||||
#
|
||||
@@ -1,26 +0,0 @@
|
||||
#
|
||||
# Automated enumeration request; designed for broadcast transfers only.
|
||||
#
|
||||
# If the node supports direct input from the user (e.g. a button), it can be used for automated Node ID assignment:
|
||||
#
|
||||
# 1. The node subscribes to this message (normally this happens while the node is in passive mode, but that's
|
||||
# not necessary). In many cases it might be wise to subscribe to this message automatically if Node ID is not
|
||||
# yet assigned (for instance if the node is not configured yet).
|
||||
#
|
||||
# 2. Some configuration tool or other node (let's call it master node) selects an appropriate Node ID and
|
||||
# broadcasts it via this message.
|
||||
#
|
||||
# 3. User performs a confirmation input on the node that is being configured (e.g. pressing a button).
|
||||
#
|
||||
# 4. The node saves the newly assigned Node ID and begins normal operation (possibly restarts).
|
||||
#
|
||||
# 5. The master node makes sure that the node with the specified Node ID is now online. If it is not, it waits till
|
||||
# the timeout expires and asks the user to repeat the procedure.
|
||||
#
|
||||
# An example use case for this feature is enumeration of actuator nodes or multiple motor controller nodes.
|
||||
#
|
||||
|
||||
uint8 TIMEOUT_INFINITE = 0
|
||||
uint8 timeout_sec # If the confirmation input was not detected in this time, enumeration request will be aborted
|
||||
|
||||
uint7 node_id # This Node ID will be assigned if the confirmation input has been detected. Must be valid.
|
||||
@@ -1,19 +0,0 @@
|
||||
#
|
||||
# Full node info request.
|
||||
# Note that all fields of the response section are byte-aligned.
|
||||
#
|
||||
|
||||
---
|
||||
|
||||
# Current node status
|
||||
NodeStatus status
|
||||
|
||||
# Version information shall not be changed while the node is running.
|
||||
SoftwareVersion software_version
|
||||
HardwareVersion hardware_version
|
||||
|
||||
# Human readable non-empty ASCII node name.
|
||||
# Empty string is not a valid node name.
|
||||
# Node name shall not be changed while the node is running.
|
||||
# The naming convention is like of Java packages (reversed internet domain names), e.g. "com.example.project.product".
|
||||
uint8[<=80] name
|
||||
@@ -1,28 +0,0 @@
|
||||
#
|
||||
# Get the implementation details of a given data type.
|
||||
#
|
||||
# Request is interpreted as follows:
|
||||
# - If the field 'name' is empty, the fields 'kind' and 'id' will be used to identify the data type.
|
||||
# - If the field 'name' is non-empty, it will be used to identify the data type; the
|
||||
# fields 'kind' and 'id' will be ignored.
|
||||
#
|
||||
|
||||
uint16 id # Ignored if 'name' is non-empty
|
||||
DataTypeKind kind # Ignored if 'name' is non-empty
|
||||
|
||||
uint8[<=80] name # Full data type name, e.g. "uavcan.protocol.GetDataTypeInfo"
|
||||
|
||||
---
|
||||
|
||||
uint64 signature # Data type signature; valid only if the data type is known (see MASK_KNOWN)
|
||||
|
||||
uint16 id # Valid only if the data type is known (see MASK_KNOWN)
|
||||
DataTypeKind kind # Ditto
|
||||
|
||||
uint8 MASK_KNOWN = 1 # This data type is defined
|
||||
uint8 MASK_SUBSCRIBED = 2 # Subscribed to messages of this type
|
||||
uint8 MASK_PUBLISHING = 4 # Publishing messages of this type
|
||||
uint8 MASK_SERVING = 8 # Providing service of this type
|
||||
uint8 mask
|
||||
|
||||
uint8[<=80] name # Full data type name; valid only if the data type is known (see MASK_KNOWN)
|
||||
@@ -1,18 +0,0 @@
|
||||
#
|
||||
# This service allows to detect type compatibility between two nodes in one request.
|
||||
# Number of items in the ID mask depends on the data type kind:
|
||||
# - 512 for services
|
||||
# - 2048 for messages
|
||||
# - All other lengths are invalid
|
||||
# If the server encounters invalid length, missing bits will be assumed zero, excessive bits will be ignored.
|
||||
#
|
||||
|
||||
DataTypeKind kind
|
||||
uint4 alignment
|
||||
bool[<=2048] known_ids
|
||||
|
||||
---
|
||||
|
||||
uint64 aggregate_signature
|
||||
uint4 alignment
|
||||
bool[<=2048] mutually_known_ids
|
||||
@@ -1,13 +0,0 @@
|
||||
#
|
||||
# Get transport statistics.
|
||||
#
|
||||
|
||||
---
|
||||
|
||||
# UAVCAN transport layer statistics
|
||||
uint48 transfers_tx
|
||||
uint48 transfers_rx
|
||||
uint48 transfer_errors
|
||||
|
||||
# CAN bus statistics, for each interface independently
|
||||
CANIfaceStats[<=3] can_iface_stats
|
||||
@@ -1,12 +0,0 @@
|
||||
#
|
||||
# Restart the node.
|
||||
# Some nodes may require restart before the new configuration will be applied.
|
||||
# The request should be rejected if magic_number does not equal MAGIC_NUMBER.
|
||||
#
|
||||
|
||||
uint40 MAGIC_NUMBER = 0xACCE551B1E
|
||||
uint40 magic_number
|
||||
|
||||
---
|
||||
|
||||
bool ok
|
||||
@@ -1,17 +0,0 @@
|
||||
#
|
||||
# Global time synchronization.
|
||||
# Preferred time is UTC.
|
||||
# Any node that publishes timestamped data must use this time reference.
|
||||
# This message is not intended for unicast transfers.
|
||||
#
|
||||
# Please refer to the specification to learn about time synchronization algorithm.
|
||||
#
|
||||
|
||||
uint16 MAX_PUBLICATION_PERIOD_MS = 1100 # Milliseconds
|
||||
uint16 MIN_PUBLICATION_PERIOD_MS = 350 # Milliseconds
|
||||
|
||||
uint16 PUBLISHER_TIMEOUT_MS = 10000 # Milliseconds
|
||||
|
||||
# Time in microseconds when the PREVIOUS GlobalTimeSync message was transmitted.
|
||||
# If this message is the first one, this field must be zero.
|
||||
uint64 previous_transmission_timestamp_usec # Microseconds
|
||||
@@ -1,19 +0,0 @@
|
||||
#
|
||||
# This message must never be published during normal operation. This message may
|
||||
# be published with high frequency (~10..100 Hz) to inform the network participants
|
||||
# that the system has encountered an unrecoverable fault (e.g. mission critical
|
||||
# node failure) and is not capable of further operation.
|
||||
#
|
||||
# Typical reaction to this message may include emergency motor/power shutdown,
|
||||
# safety parachute deployment, etc. Nodes that are expected to react to this message
|
||||
# should wait for at least MIN_MESSAGES subsequent messages with any reason text
|
||||
# from any sender published with the interval no higher than MAX_INTERVAL_MS before
|
||||
# undertaking any emergency actions.
|
||||
#
|
||||
|
||||
uint8 MIN_MESSAGES = 3
|
||||
uint16 MAX_INTERVAL_MS = 500
|
||||
uint16 BROADCASTING_INTERVAL_MS = 100
|
||||
|
||||
# Short description that would fit a single CAN frame
|
||||
uint8[<=7] reason_text
|
||||
@@ -1,7 +0,0 @@
|
||||
#
|
||||
# Single CAN iface statistics
|
||||
#
|
||||
|
||||
uint48 frames_tx
|
||||
uint48 frames_rx
|
||||
uint48 errors
|
||||
@@ -1,7 +0,0 @@
|
||||
#
|
||||
# Data type kind (message or service).
|
||||
#
|
||||
|
||||
uint8 SERVICE = 0
|
||||
uint8 MESSAGE = 1
|
||||
uint8 value
|
||||
@@ -1,15 +0,0 @@
|
||||
#
|
||||
# Nested type.
|
||||
# Generic hardware version information.
|
||||
# These values should remain unchanged for the device's lifetime.
|
||||
#
|
||||
|
||||
uint8 major
|
||||
uint8 minor
|
||||
|
||||
# All zeros is not a valid UID.
|
||||
# If filled with zeros, assume that the value is undefined.
|
||||
uint8[16] unique_id
|
||||
|
||||
# Certificate of authenticity (COA) of the hardware, 255 bytes max.
|
||||
uint8[<256] certificate_of_authenticity
|
||||
@@ -1,19 +0,0 @@
|
||||
#
|
||||
# Nested type.
|
||||
# Generic software version information.
|
||||
#
|
||||
|
||||
# Primary version numbers for humans. Can be both set to zeros if not available.
|
||||
uint8 major
|
||||
uint8 minor
|
||||
|
||||
# This mask indicates which optional fields are set.
|
||||
uint8 OPTIONAL_FIELD_MASK_VCS_COMMIT = 1
|
||||
uint8 OPTIONAL_FIELD_MASK_IMAGE_CRC = 2
|
||||
uint8 optional_field_mask
|
||||
|
||||
# VCS commit hash or revision number, e.g. git short commit hash. Useful for the developers. Optional.
|
||||
uint32 vcs_commit
|
||||
|
||||
# CRC-64-WE of the firmware image. CRC function is the same as in the data type signature. Optional.
|
||||
uint64 image_crc
|
||||
@@ -1,14 +0,0 @@
|
||||
#
|
||||
# Generic named parameter (key/value pair).
|
||||
#
|
||||
|
||||
#
|
||||
# Integers are exactly representable in the range (-2^24, 2^24) which is (-16'777'216, 16'777'216).
|
||||
#
|
||||
float32 value
|
||||
|
||||
#
|
||||
# Tail array optimization is enabled, so if key length does not exceed 4 characters,
|
||||
# the whole message can fit into one CAN frame.
|
||||
#
|
||||
uint8[<=34] key
|
||||
@@ -1,7 +0,0 @@
|
||||
#
|
||||
# Generic log message.
|
||||
#
|
||||
|
||||
LogLevel level
|
||||
uint8[<32] source
|
||||
uint8[<94] text
|
||||
@@ -1,13 +0,0 @@
|
||||
#
|
||||
# Start HIL simulation for the specified components.
|
||||
# The request should be rejected if magic_number does not equal MAGIC_NUMBER.
|
||||
#
|
||||
|
||||
uint40 MAGIC_NUMBER = 0xACCE551B1E
|
||||
uint40 magic_number
|
||||
|
||||
uint64 component_mask # Components are application defined
|
||||
|
||||
---
|
||||
|
||||
bool ok
|
||||
@@ -1,9 +0,0 @@
|
||||
#
|
||||
# Log message severity
|
||||
#
|
||||
|
||||
uint3 DEBUG = 0
|
||||
uint3 INFO = 1
|
||||
uint3 WARNING = 2
|
||||
uint3 ERROR = 3
|
||||
uint3 value
|
||||
@@ -1,69 +0,0 @@
|
||||
#
|
||||
# This message is used for dynamic Node ID allocation. This algorithm is an optional extension to the UAVCAN
|
||||
# specification, and it is not mandatory to support it.
|
||||
#
|
||||
# On the client's side the protocol is defined through the following set of rules:
|
||||
#
|
||||
# Rule A. On initialization:
|
||||
# 1. The client subscribes to this message.
|
||||
# 2. The client starts the Request Timer with interval of DEFAULT_REQUEST_INTERVAL_MS.
|
||||
#
|
||||
# Rule B. On expiration of Request Timer:
|
||||
# 1. Request Timer restarts.
|
||||
# 2. The client broadcasts a first-stage Allocation request message, where the fields are assigned following values:
|
||||
# node_id - preferred node ID, or zero if the client doesn't have any preference
|
||||
# first_part_of_unique_id - true
|
||||
# unique_id - first MAX_LENGTH_OF_UNIQUE_ID_IN_REQUEST bytes of unique ID
|
||||
#
|
||||
# Rule C. On any Allocation message, even if other rules also match:
|
||||
# 1. Request Timer restarts.
|
||||
#
|
||||
# Rule D. On an Allocation message WHERE (source node ID is non-anonymous) AND (client's unique ID starts with the
|
||||
# bytes available in the field unique_id) AND (unique_id is less than 16 bytes long):
|
||||
# 1. The client broadcasts a second-stage Allocation request message, where the fields are assigned following values:
|
||||
# node_id - same value as in the first-stage
|
||||
# first_part_of_unique_id - false
|
||||
# unique_id - at most MAX_LENGTH_OF_UNIQUE_ID_IN_REQUEST bytes of local unique ID with an offset
|
||||
# equal to number of bytes in the received unique ID
|
||||
#
|
||||
# Rule E. On an Allocation message WHERE (source node ID is non-anonymous) AND (unique_id fully matches client's
|
||||
# unique ID) AND (node_id in the received message is not zero):
|
||||
# 1. Request Timer stops.
|
||||
# 2. The client initializes its node_id with the received value.
|
||||
# 3. The client terminates subscription to Allocation messages.
|
||||
# 4. Exit.
|
||||
#
|
||||
|
||||
#
|
||||
# Recommended request transmission period.
|
||||
#
|
||||
uint16 DEFAULT_REQUEST_PERIOD_MS = 1000
|
||||
|
||||
#
|
||||
# Server will reset its state if there was no follow-up request in this amount of time.
|
||||
#
|
||||
uint16 FOLLOWUP_TIMEOUT_MS = 500
|
||||
|
||||
#
|
||||
# Any request message can accommodate no more than this number of bytes of unique ID.
|
||||
# This limitation is needed to ensure that all request transfers are single-frame.
|
||||
#
|
||||
uint8 MAX_LENGTH_OF_UNIQUE_ID_IN_REQUEST = 7
|
||||
|
||||
#
|
||||
# If transfer is anonymous, this is the preferred ID.
|
||||
# If transfer is non-anonymous, this is allocated ID.
|
||||
#
|
||||
uint7 node_id
|
||||
|
||||
#
|
||||
# If transfer is anonymous, this field indicates first-stage request.
|
||||
# If transfer is non-anonymous, this field should be ignored.
|
||||
#
|
||||
bool first_part_of_unique_id
|
||||
|
||||
#
|
||||
# If transfer is anonymous, this array must not contain more than MAX_LENGTH_OF_UNIQUE_ID_IN_REQUEST items.
|
||||
# Note that array is tail-optimized, i.e. it will not be prepended with length field.
|
||||
#
|
||||
uint8[<=16] unique_id
|
||||
@@ -1,20 +0,0 @@
|
||||
#
|
||||
# This message is used by allocation servers to find each other's node ID.
|
||||
# Please refer to the specification for details.
|
||||
#
|
||||
|
||||
#
|
||||
# This message should be broadcasted by the server at this interval until all other servers are discovered.
|
||||
#
|
||||
uint16 BROADCASTING_INTERVAL_MS = 1000
|
||||
|
||||
#
|
||||
# Number of servers in the cluster as configured on the sender.
|
||||
#
|
||||
uint8 configured_cluster_size
|
||||
|
||||
#
|
||||
# Node ID of servers that are known to the publishing server, including the publishing server itself.
|
||||
# Capacity of this array defines maximum size of the server cluster.
|
||||
#
|
||||
uint8[<=5] known_nodes
|
||||
@@ -1,33 +0,0 @@
|
||||
#
|
||||
# This type is a part of the Raft consensus algorithm.
|
||||
# Please refer to the specification for details.
|
||||
#
|
||||
|
||||
#
|
||||
# Given min election timeout and cluster size, the maximum recommended request interval can be derived as follows:
|
||||
# max recommended request interval = (min election timeout) / 2 requests / (cluster size - 1)
|
||||
# The equation assumes that the Leader requests one Follower at a time, so that there's at most one pending call
|
||||
# at any moment. Such behavior is optimal as it creates uniform bus load, but it is actually implementation-specific.
|
||||
# Obviously, request interval can be lower than that if needed, but higher values are not recommended as they may
|
||||
# cause Followers to initiate premature elections in case of intensive frame losses or delays.
|
||||
#
|
||||
# Real timeout is randomized in the range (MIN, MAX], according to the Raft paper.
|
||||
#
|
||||
uint16 DEFAULT_MIN_ELECTION_TIMEOUT_MS = 4000
|
||||
uint16 DEFAULT_MAX_ELECTION_TIMEOUT_MS = 6000
|
||||
|
||||
uint32 term
|
||||
uint32 prev_log_term
|
||||
uint8 prev_log_index
|
||||
uint8 leader_commit
|
||||
|
||||
#
|
||||
# Worst-case replication time per Follower can be computed as:
|
||||
# worst replication time = (127 log entries) * (2 trips of next_index) * (request interval per Follower)
|
||||
#
|
||||
Entry[<=1] entries
|
||||
|
||||
---
|
||||
|
||||
uint32 term
|
||||
bool success
|
||||
@@ -1,13 +0,0 @@
|
||||
#
|
||||
# This type is a part of the Raft consensus algorithm.
|
||||
# Please refer to the specification for details.
|
||||
#
|
||||
|
||||
uint32 term
|
||||
uint32 last_log_term
|
||||
uint8 last_log_index
|
||||
|
||||
---
|
||||
|
||||
uint32 term
|
||||
bool vote_granted
|
||||
@@ -1,10 +0,0 @@
|
||||
#
|
||||
# One dynamic node ID allocation entry.
|
||||
# This type is a part of the Raft consensus algorithm.
|
||||
# Please refer to the specification for details.
|
||||
#
|
||||
|
||||
uint32 term
|
||||
uint8[16] unique_id
|
||||
uint7 node_id
|
||||
bool alignment
|
||||
@@ -1,30 +0,0 @@
|
||||
#
|
||||
# This service initiates firmware update on a remote node.
|
||||
#
|
||||
# The node that is being updated (slave) will retrieve the firmware image file 'image_file_remote_path' from the node
|
||||
# 'source_node_id' using the file read service, then it will update the firmware and reboot. Alternatively, this
|
||||
# service can be used to invoke an alternative CAN bootloader (e.g. STM32 system memory bootloader), in which case
|
||||
# the field 'image_file_remote_path' must be empty.
|
||||
#
|
||||
# The slave can explicitly reject this request if it is not possible to update the firmware at the moment
|
||||
# (e.g. if the node is busy).
|
||||
#
|
||||
# If the slave node accepts this request, the initiator will get a response immediately, before the update process
|
||||
# actually begins.
|
||||
#
|
||||
# While the firmware is being updated, the slave should set its status code (uavcan.protocol.NodeStatus.status_code)
|
||||
# to STATUS_INITIALIZING.
|
||||
#
|
||||
|
||||
uint8 source_node_id # If there is an invalid value (e.g. zero), the caller Node ID will be used instead
|
||||
Path image_file_remote_path # Empty to invoke an alternative bootloader
|
||||
|
||||
---
|
||||
|
||||
uint8 ERROR_OK = 0
|
||||
uint8 ERROR_INVALID_MODE = 1 # Cannot perform the update right now (e.g. the vehicle is operating)
|
||||
uint8 ERROR_IN_PROGRESS = 2 # Firmware update is already in progess, and the slave doesn't want to restart
|
||||
uint8 ERROR_UNKNOWN = 255
|
||||
|
||||
uint8 error
|
||||
uint8[<128] optional_error_message # Detailed error description
|
||||
@@ -1,16 +0,0 @@
|
||||
#
|
||||
# Request info about a remote file system entry (file, directory, etc).
|
||||
#
|
||||
# CRC computation algorithm is the same as for DSDL signature (refer to the protocol specification). The CRC field
|
||||
# should be set to zero for directories.
|
||||
#
|
||||
# Size is the file size in bytes. It should be set to zero for directories.
|
||||
#
|
||||
|
||||
Path path
|
||||
|
||||
---
|
||||
|
||||
uint40 size # Undefined for directories
|
||||
Error error
|
||||
EntryType entry_type
|
||||
@@ -1,16 +0,0 @@
|
||||
#
|
||||
# This service can be used to retrieve remote directory listing, one entry per request.
|
||||
# The client should query each entry independently, iterating 'entry_index' from 0 until the last entry is passed,
|
||||
# in which case the server will report that there is no such entry (via the fields 'entry_type' and 'error').
|
||||
# The entry_index shall be applied to the ordered list of directory entries (e.g. alphabetically ordered). The exact
|
||||
# sorting criteria does not matter as long as it provides the same ordering for subsequent service calls.
|
||||
#
|
||||
|
||||
uint32 entry_index
|
||||
Path directory_path
|
||||
|
||||
---
|
||||
|
||||
Error error
|
||||
EntryType entry_type
|
||||
Path entry_full_path # Ignored/Empty if such entry does not exist.
|
||||
@@ -1,10 +0,0 @@
|
||||
#
|
||||
# Delete remote file system entry.
|
||||
# If the remote entry is a directory, all nested entries will be removed too.
|
||||
#
|
||||
|
||||
Path path
|
||||
|
||||
---
|
||||
|
||||
Error error
|
||||
@@ -1,21 +0,0 @@
|
||||
#
|
||||
# Read file from a remote node.
|
||||
#
|
||||
# There are two possible outcomes of a successful service call:
|
||||
# 1. Data array size equals its capacity. This means that the end of the file is not reached yet.
|
||||
# 2. Data array size is less than its capacity, possibly zero. This means that the end of file is reached.
|
||||
#
|
||||
# Thus, if the client needs to fetch the entire file, it should repeatedly call this service while increasing the
|
||||
# offset, until incomplete data is returned.
|
||||
#
|
||||
# If the object pointed by 'path' cannot be read (e.g. it is a directory or it does not exist), appropriate error code
|
||||
# will be returned, and data array will be empty.
|
||||
#
|
||||
|
||||
uint40 offset
|
||||
Path path
|
||||
|
||||
---
|
||||
|
||||
Error error
|
||||
uint8[<=256] data
|
||||
@@ -1,24 +0,0 @@
|
||||
#
|
||||
# Write a remote file.
|
||||
# The server shall place the contents of the field 'data' into the file pointed by 'path' at the offset specified by
|
||||
# the field 'offset'.
|
||||
#
|
||||
# When writing a file, the client should repeatedly call this service with data while advancing offset until the file
|
||||
# is written completely. When write is complete, the client shall call the service one last time, with the offset
|
||||
# set to the size of the file and with the data field empty, which will signal the server that the write operation is
|
||||
# complete.
|
||||
#
|
||||
# When the write operation is complete, the server shall truncate the resulting file past the specified offset.
|
||||
#
|
||||
# Server implementation advice:
|
||||
# It is recommended to implement proper handling of concurrent writes to the same file from different clients, for
|
||||
# example by means of creating a staging area for uncompleted writes (like FTP servers do).
|
||||
#
|
||||
|
||||
uint40 offset
|
||||
Path path
|
||||
uint8[<=192] data
|
||||
|
||||
---
|
||||
|
||||
Error error
|
||||
@@ -1,13 +0,0 @@
|
||||
#
|
||||
# Nested type.
|
||||
# Represents the type of the file system entry (e.g. file or directory).
|
||||
# If such entry does not exist, 'flags' must be set to zero.
|
||||
#
|
||||
|
||||
uint8 FLAG_FILE = 1
|
||||
uint8 FLAG_DIRECTORY = 2
|
||||
uint8 FLAG_SYMLINK = 4
|
||||
uint8 FLAG_READABLE = 8
|
||||
uint8 FLAG_WRITEABLE = 16
|
||||
|
||||
uint8 flags
|
||||
@@ -1,19 +0,0 @@
|
||||
#
|
||||
# Nested type.
|
||||
# File operation result code.
|
||||
#
|
||||
|
||||
int16 OK = 0
|
||||
int16 UNKNOWN_ERROR = 32767
|
||||
|
||||
# These error codes match some standard UNIX errno values
|
||||
int16 NOT_FOUND = 2
|
||||
int16 IO_ERROR = 5
|
||||
int16 ACCESS_DENIED = 13
|
||||
int16 IS_DIRECTORY = 21 # I.e. attempt to read/write on a path that points to a directory
|
||||
int16 INVALID_VALUE = 22 # E.g. file name is not valid for the target file system
|
||||
int16 FILE_TOO_LARGE = 27
|
||||
int16 OUT_OF_SPACE = 28
|
||||
int16 NOT_IMPLEMENTED = 38
|
||||
|
||||
int16 value
|
||||
@@ -1,9 +0,0 @@
|
||||
#
|
||||
# Nested type.
|
||||
# File system path in UTF8.
|
||||
# The only valid separator is forward slash.
|
||||
#
|
||||
|
||||
uint8 SEPARATOR = '/'
|
||||
|
||||
uint8[<=200] path
|
||||
@@ -1,29 +0,0 @@
|
||||
#
|
||||
# Service to control the node configuration.
|
||||
#
|
||||
# SAVE operation instructs the remote node to save the current configuration parameters to the non-volatile
|
||||
# storage. The node may require a restart in order for some changes to take effect.
|
||||
#
|
||||
# ERASE operation instructs the remote node to clear its configuration storage and reinitialize the parameters
|
||||
# with their default values. The node may require a restart in order for some changes to take effect.
|
||||
#
|
||||
# Other opcodes may be added in the future (for example, an opcode for switching between multiple configurations).
|
||||
#
|
||||
|
||||
uint8 OPCODE_SAVE = 0 # Save all parameters to non-volatile storage.
|
||||
uint8 OPCODE_ERASE = 1 # Clear the non-volatile storage; some changes may take effect only after reboot.
|
||||
uint8 opcode
|
||||
|
||||
#
|
||||
# Reserved, keep zero.
|
||||
#
|
||||
int48 argument
|
||||
|
||||
---
|
||||
|
||||
#
|
||||
# Reserved, keep zero.
|
||||
#
|
||||
int48 argument
|
||||
|
||||
bool ok
|
||||
@@ -1,34 +0,0 @@
|
||||
#
|
||||
# Get or set a parameter by name or by index.
|
||||
# Note that access by index should only be used to retreive the list of parameters; it is higly
|
||||
# discouraged to use it for anything else, because persistent ordering is not guaranteed.
|
||||
#
|
||||
|
||||
# If set - parameter will be assigned this value, then the new value will be returned.
|
||||
# If not set - current parameter value will be returned.
|
||||
# Refer to the definition of Value for details.
|
||||
Value value
|
||||
|
||||
# Index of the parameter starting from 0; ignored if name is nonempty.
|
||||
# Use index only to retrieve the list of parameters.
|
||||
uint8 index
|
||||
|
||||
# Name of the parameter; always preferred over index if nonempty.
|
||||
uint8[<=92] name
|
||||
|
||||
---
|
||||
|
||||
# Actual parameter value.
|
||||
# For set requests, it should contain the actual parameter value after the set request was
|
||||
# executed. The objective is to let the client know if the value could not be updated, e.g.
|
||||
# due to its range violation, etc.
|
||||
# Empty value indicates that there is no such parameter.
|
||||
Value value
|
||||
|
||||
Value default_value # Optional
|
||||
|
||||
NumericValue max_value # Optional, not applicable for bool/string
|
||||
NumericValue min_value # Optional, not applicable for bool/string
|
||||
|
||||
# Empty name in response indicates that there is no such parameter
|
||||
uint8[<=92] name
|
||||
@@ -1,8 +0,0 @@
|
||||
#
|
||||
# Numeric-only value.
|
||||
# The actual type should be inferred from the available values, as described below.
|
||||
# If none of the values below are present, the value is considered empty.
|
||||
#
|
||||
|
||||
int64[<=1] value_int # Preferred over float if ambiguous
|
||||
float32[<=1] value_float # Only if int is empty
|
||||
@@ -1,5 +0,0 @@
|
||||
#
|
||||
# This type is nested in Value.
|
||||
#
|
||||
|
||||
uint8[<=127] value
|
||||
@@ -1,10 +0,0 @@
|
||||
#
|
||||
# Single parameter value.
|
||||
# The actual type should be inferred from the available values, as described below.
|
||||
# If none of the values below are present, the value is considered empty.
|
||||
#
|
||||
|
||||
bool[<=1] value_bool # Preferred over int, float and string if ambiguous
|
||||
int64[<=1] value_int # Preferred over float and string if ambiguous
|
||||
float32[<=1] value_float # Preferred over string if ambiguous
|
||||
String[<=1] value_string # This one will be used only if all above are empty
|
||||
@@ -1,11 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
#
|
||||
|
||||
'''
|
||||
Python UAVCAN package.
|
||||
Supported Python versions: 3.2+, 2.7.
|
||||
Currently it implements only a DSDL parser (refer to the submodule 'dsdl').
|
||||
'''
|
||||
|
||||
from . import dsdl
|
||||
@@ -1,19 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
#
|
||||
|
||||
'''
|
||||
This module implements a fully compliant UAVCAN DSDL parser.
|
||||
Please read the specs at http://uavcan.org.
|
||||
'''
|
||||
|
||||
from .parser import Parser, parse_namespaces, \
|
||||
Type, PrimitiveType, ArrayType, CompoundType, \
|
||||
Attribute, Field, Constant
|
||||
|
||||
from .common import DsdlException
|
||||
|
||||
__all__ = ['Parser', 'parse_namespaces',
|
||||
'Type', 'PrimitiveType', 'ArrayType', 'CompoundType',
|
||||
'Attribute', 'Field', 'Constant',
|
||||
'DsdlException']
|
||||
@@ -1,33 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
#
|
||||
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
import os
|
||||
|
||||
class DsdlException(Exception):
|
||||
'''
|
||||
This exception is raised in case of a parser failure.
|
||||
Fields:
|
||||
file Source file path where the error has occurred. Optional, will be None if unknown.
|
||||
line Source file line number where the error has occurred. Optional, will be None if unknown.
|
||||
'''
|
||||
def __init__(self, text, file=None, line=None):
|
||||
Exception.__init__(self, text)
|
||||
self.file = file
|
||||
self.line = line
|
||||
|
||||
def __str__(self):
|
||||
'''Returns nicely formatted error string in GCC-like format (can be parsed by e.g. Eclipse error parser)'''
|
||||
if self.file and self.line:
|
||||
return '%s:%d: %s' % (pretty_filename(self.file), self.line, Exception.__str__(self))
|
||||
if self.file:
|
||||
return '%s: %s' % (pretty_filename(self.file), Exception.__str__(self))
|
||||
return Exception.__str__(self)
|
||||
|
||||
|
||||
def pretty_filename(filename):
|
||||
'''Returns a nice human readable path to 'filename'.'''
|
||||
a = os.path.abspath(filename)
|
||||
r = os.path.relpath(filename)
|
||||
return a if '..' in r else r
|
||||
@@ -1,652 +0,0 @@
|
||||
#
|
||||
# UAVCAN DSDL file parser
|
||||
#
|
||||
# Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
#
|
||||
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
import os, re, logging
|
||||
from io import StringIO
|
||||
from .signature import compute_signature
|
||||
from .common import DsdlException, pretty_filename
|
||||
from .type_limits import get_unsigned_integer_range, get_signed_integer_range, get_float_range
|
||||
|
||||
# Python 2.7 compatibility
|
||||
try:
|
||||
str = unicode
|
||||
except NameError:
|
||||
pass
|
||||
try:
|
||||
long(1)
|
||||
except NameError:
|
||||
long = int
|
||||
|
||||
MAX_FULL_TYPE_NAME_LEN = 80
|
||||
|
||||
SERVICE_DATA_TYPE_ID_MAX = 511
|
||||
MESSAGE_DATA_TYPE_ID_MAX = 2047
|
||||
|
||||
MAX_SERVICE_STRUCT_LEN_BYTES = 439
|
||||
MAX_MESSAGE_STRUCT_LEN_BYTES = 126 # Broadcast
|
||||
|
||||
class Type:
|
||||
'''
|
||||
Common type description. The specialized type description classes inherit from this one.
|
||||
Fields:
|
||||
full_name Full type name string, e.g. "uavcan.protocol.NodeStatus"
|
||||
category Any CATEGORY_*
|
||||
'''
|
||||
CATEGORY_PRIMITIVE = 0
|
||||
CATEGORY_ARRAY = 1
|
||||
CATEGORY_COMPOUND = 2
|
||||
|
||||
def __init__(self, full_name, category):
|
||||
self.full_name = str(full_name)
|
||||
self.category = category
|
||||
|
||||
def __str__(self):
|
||||
return self.get_normalized_definition()
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
class PrimitiveType(Type):
|
||||
'''
|
||||
Primitive type description, e.g. bool or float16.
|
||||
Fields:
|
||||
kind Any KIND_*
|
||||
bitlen Bit length, 1 to 64
|
||||
cast_mode Any CAST_MODE_*
|
||||
value_range Tuple containing min and max values: (min, max)
|
||||
'''
|
||||
KIND_BOOLEAN = 0
|
||||
KIND_UNSIGNED_INT = 1
|
||||
KIND_SIGNED_INT = 2
|
||||
KIND_FLOAT = 3
|
||||
|
||||
CAST_MODE_SATURATED = 0
|
||||
CAST_MODE_TRUNCATED = 1
|
||||
|
||||
def __init__(self, kind, bitlen, cast_mode):
|
||||
self.kind = kind
|
||||
self.bitlen = bitlen
|
||||
self.cast_mode = cast_mode
|
||||
Type.__init__(self, self.get_normalized_definition(), Type.CATEGORY_PRIMITIVE)
|
||||
self.value_range = {
|
||||
PrimitiveType.KIND_BOOLEAN: get_unsigned_integer_range,
|
||||
PrimitiveType.KIND_UNSIGNED_INT: get_unsigned_integer_range,
|
||||
PrimitiveType.KIND_SIGNED_INT: get_signed_integer_range,
|
||||
PrimitiveType.KIND_FLOAT: get_float_range
|
||||
}[self.kind](bitlen)
|
||||
|
||||
def get_normalized_definition(self):
|
||||
'''Please refer to the specification for details about normalized definitions.'''
|
||||
cast_mode = 'saturated' if self.cast_mode == PrimitiveType.CAST_MODE_SATURATED else 'truncated'
|
||||
primary_type = {
|
||||
PrimitiveType.KIND_BOOLEAN: 'bool',
|
||||
PrimitiveType.KIND_UNSIGNED_INT: 'uint' + str(self.bitlen),
|
||||
PrimitiveType.KIND_SIGNED_INT: 'int' + str(self.bitlen),
|
||||
PrimitiveType.KIND_FLOAT: 'float' + str(self.bitlen)
|
||||
}[self.kind]
|
||||
return cast_mode + ' ' + primary_type
|
||||
|
||||
def validate_value_range(self, value):
|
||||
'''Checks value range, throws DsdlException if the value cannot be represented by this type.'''
|
||||
low, high = self.value_range
|
||||
if not low <= value <= high:
|
||||
error('Value [%s] is out of range %s', value, self.value_range)
|
||||
|
||||
def get_max_bitlen(self):
|
||||
'''Returns type bit length.'''
|
||||
return self.bitlen
|
||||
|
||||
class ArrayType(Type):
|
||||
'''
|
||||
Array type description, e.g. float32[8], uint12[<34].
|
||||
Fields:
|
||||
value_type Description of the array value type; the type of this field inherits Type, e.g. PrimitiveType
|
||||
mode Any MODE_*
|
||||
max_size Maximum number of elements in the array
|
||||
'''
|
||||
MODE_STATIC = 0
|
||||
MODE_DYNAMIC = 1
|
||||
|
||||
def __init__(self, value_type, mode, max_size):
|
||||
self.value_type = value_type
|
||||
self.mode = mode
|
||||
self.max_size = max_size
|
||||
Type.__init__(self, self.get_normalized_definition(), Type.CATEGORY_ARRAY)
|
||||
|
||||
def get_normalized_definition(self):
|
||||
'''Please refer to the specification for details about normalized definitions.'''
|
||||
typedef = self.value_type.get_normalized_definition()
|
||||
return ('%s[<=%d]' if self.mode == ArrayType.MODE_DYNAMIC else '%s[%d]') % (typedef, self.max_size)
|
||||
|
||||
def get_max_bitlen(self):
|
||||
'''Returns total maximum bit length of the array, including length field if applicable.'''
|
||||
payload_max_bitlen = self.max_size * self.value_type.get_max_bitlen()
|
||||
return {
|
||||
self.MODE_DYNAMIC: payload_max_bitlen + self.max_size.bit_length(),
|
||||
self.MODE_STATIC: payload_max_bitlen
|
||||
}[self.mode]
|
||||
|
||||
class CompoundType(Type):
|
||||
'''
|
||||
Compound type description, e.g. uavcan.protocol.NodeStatus.
|
||||
Fields:
|
||||
source_file Path to the DSDL definition file for this type
|
||||
default_dtid Default Data Type ID, if specified, None otherwise
|
||||
kind Any KIND_*
|
||||
source_text Raw DSDL definition text (as is, with comments and the original formatting)
|
||||
|
||||
Fields if kind == KIND_SERVICE:
|
||||
request_fields Request struct field list, the type of each element is Field
|
||||
response_fields Response struct field list
|
||||
request_constants Request struct constant list, the type of each element is Constant
|
||||
response_constants Response struct constant list
|
||||
|
||||
Fields if kind == KIND_MESSAGE:
|
||||
fields Field list, the type of each element is Field
|
||||
constants Constant list, the type of each element is Constant
|
||||
|
||||
Extra methods if kind == KIND_SERVICE:
|
||||
get_max_bitlen_request() Returns maximum total bit length for the serialized request struct
|
||||
get_max_bitlen_response() Same for the response struct
|
||||
|
||||
Extra methods if kind == KIND_MESSAGE:
|
||||
get_max_bitlen() Returns maximum total bit length for the serialized struct
|
||||
'''
|
||||
KIND_SERVICE = 0
|
||||
KIND_MESSAGE = 1
|
||||
|
||||
def __init__(self, full_name, kind, source_file, default_dtid, source_text):
|
||||
Type.__init__(self, full_name, Type.CATEGORY_COMPOUND)
|
||||
self.source_file = source_file
|
||||
self.default_dtid = default_dtid
|
||||
self.kind = kind
|
||||
self.source_text = source_text
|
||||
max_bitlen_sum = lambda fields: sum([x.type.get_max_bitlen() for x in fields])
|
||||
if kind == CompoundType.KIND_SERVICE:
|
||||
self.request_fields = []
|
||||
self.response_fields = []
|
||||
self.request_constants = []
|
||||
self.response_constants = []
|
||||
self.get_max_bitlen_request = lambda: max_bitlen_sum(self.request_fields)
|
||||
self.get_max_bitlen_response = lambda: max_bitlen_sum(self.response_fields)
|
||||
elif kind == CompoundType.KIND_MESSAGE:
|
||||
self.fields = []
|
||||
self.constants = []
|
||||
self.get_max_bitlen = lambda: max_bitlen_sum(self.fields)
|
||||
else:
|
||||
error('Compound type of unknown kind [%s]', kind)
|
||||
|
||||
def get_dsdl_signature_source_definition(self):
|
||||
'''
|
||||
Returns normalized DSDL definition text.
|
||||
Please refer to the specification for details about normalized DSDL definitions.
|
||||
'''
|
||||
txt = StringIO()
|
||||
txt.write(self.full_name + '\n')
|
||||
adjoin = lambda attrs: txt.write('\n'.join(x.get_normalized_definition() for x in attrs) + '\n')
|
||||
if self.kind == CompoundType.KIND_SERVICE:
|
||||
adjoin(self.request_fields)
|
||||
txt.write('\n---\n')
|
||||
adjoin(self.response_fields)
|
||||
elif self.kind == CompoundType.KIND_MESSAGE:
|
||||
adjoin(self.fields)
|
||||
else:
|
||||
error('Compound type of unknown kind [%s]', self.kind)
|
||||
return txt.getvalue().strip().replace('\n\n\n', '\n').replace('\n\n', '\n')
|
||||
|
||||
def get_dsdl_signature(self):
|
||||
'''
|
||||
Computes DSDL signature of this type.
|
||||
Please refer to the specification for details about signatures.
|
||||
'''
|
||||
return compute_signature(self.get_dsdl_signature_source_definition())
|
||||
|
||||
def get_normalized_definition(self):
|
||||
'''Returns full type name string, e.g. "uavcan.protocol.NodeStatus"'''
|
||||
return self.full_name
|
||||
|
||||
|
||||
class Attribute:
|
||||
'''
|
||||
Base class of an attribute description.
|
||||
Fields:
|
||||
type Attribute type description, the type of this field inherits the class Type, e.g. PrimitiveType
|
||||
name Attribute name string
|
||||
'''
|
||||
def __init__(self, type, name): # @ReservedAssignment
|
||||
self.type = type
|
||||
self.name = name
|
||||
|
||||
def __str__(self):
|
||||
return self.get_normalized_definition()
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
class Field(Attribute):
|
||||
'''
|
||||
Field description.
|
||||
Does not add new fields to Attribute.
|
||||
'''
|
||||
def get_normalized_definition(self):
|
||||
return '%s %s' % (self.type.get_normalized_definition(), self.name)
|
||||
|
||||
class Constant(Attribute):
|
||||
'''
|
||||
Constant description.
|
||||
Fields:
|
||||
init_expression Constant initialization expression string, e.g. "2+2" or "'\x66'"
|
||||
value Computed result of the initialization expression in the final type (e.g. int, float)
|
||||
string_value Computed result of the initialization expression as string
|
||||
'''
|
||||
def __init__(self, type, name, init_expression, value): # @ReservedAssignment
|
||||
Attribute.__init__(self, type, name)
|
||||
self.init_expression = init_expression
|
||||
self.value = value
|
||||
self.string_value = repr(value)
|
||||
if isinstance(value, long):
|
||||
self.string_value = self.string_value.replace('L', '')
|
||||
|
||||
def get_normalized_definition(self):
|
||||
return '%s %s = %s' % (self.type.get_normalized_definition(), self.name, self.init_expression)
|
||||
|
||||
|
||||
class Parser:
|
||||
'''
|
||||
DSDL parser logic. Do not use this class directly; use the helper function instead.
|
||||
'''
|
||||
LOGGER_NAME = 'dsdl_parser'
|
||||
|
||||
def __init__(self, search_dirs):
|
||||
self.search_dirs = validate_search_directories(search_dirs)
|
||||
self.log = logging.getLogger(Parser.LOGGER_NAME)
|
||||
|
||||
def _namespace_from_filename(self, filename):
|
||||
search_dirs = sorted(map(os.path.abspath, self.search_dirs)) # Nested last
|
||||
filename = os.path.abspath(filename)
|
||||
for dirname in search_dirs:
|
||||
root_ns = dirname.split(os.path.sep)[-1]
|
||||
if filename.startswith(dirname):
|
||||
dir_len = len(dirname)
|
||||
basename_len = len(os.path.basename(filename))
|
||||
ns = filename[dir_len:-basename_len]
|
||||
ns = (root_ns + '.' + ns.replace(os.path.sep, '.').strip('.')).strip('.')
|
||||
validate_namespace_name(ns)
|
||||
return ns
|
||||
error('File [%s] was not found in search directories', filename)
|
||||
|
||||
def _full_typename_and_dtid_from_filename(self, filename):
|
||||
basename = os.path.basename(filename)
|
||||
items = basename.split('.')
|
||||
if (len(items) != 2 and len(items) != 3) or items[-1] != 'uavcan':
|
||||
error('Invalid file name [%s]; expected pattern: [<default-dtid>.]<short-type-name>.uavcan', basename)
|
||||
if len(items) == 2:
|
||||
default_dtid, name = None, items[0]
|
||||
else:
|
||||
default_dtid, name = items[0], items[1]
|
||||
try:
|
||||
default_dtid = int(default_dtid)
|
||||
except ValueError:
|
||||
error('Invalid default data type ID [%s]', default_dtid)
|
||||
full_name = self._namespace_from_filename(filename) + '.' + name
|
||||
validate_compound_type_full_name(full_name)
|
||||
return full_name, default_dtid
|
||||
|
||||
def _locate_compound_type_definition(self, referencing_filename, typename):
|
||||
def locate_namespace_directory(namespace):
|
||||
namespace_components = namespace.split('.')
|
||||
root_namespace, sub_namespace_components = namespace_components[0], namespace_components[1:]
|
||||
for directory in self.search_dirs:
|
||||
if directory.split(os.path.sep)[-1] == root_namespace:
|
||||
return os.path.join(directory, *sub_namespace_components)
|
||||
error('Unknown namespace [%s]', namespace)
|
||||
|
||||
if '.' not in typename:
|
||||
current_namespace = self._namespace_from_filename(referencing_filename)
|
||||
full_typename = current_namespace + '.' + typename
|
||||
else:
|
||||
full_typename = typename
|
||||
namespace = '.'.join(full_typename.split('.')[:-1])
|
||||
directory = locate_namespace_directory(namespace)
|
||||
self.log.debug('Searching for [%s] in [%s]', full_typename, directory)
|
||||
|
||||
for fn in os.listdir(directory):
|
||||
fn = os.path.join(directory, fn)
|
||||
if os.path.isfile(fn):
|
||||
try:
|
||||
fn_full_typename, _dtid = self._full_typename_and_dtid_from_filename(fn)
|
||||
if full_typename == fn_full_typename:
|
||||
return fn
|
||||
except Exception as ex:
|
||||
self.log.debug('Unknown file [%s], skipping... [%s]', pretty_filename(fn), ex)
|
||||
error('Type definition not found [%s]', typename)
|
||||
|
||||
def _parse_array_type(self, filename, value_typedef, size_spec, cast_mode):
|
||||
self.log.debug('Parsing the array value type [%s]...', value_typedef)
|
||||
value_type = self._parse_type(filename, value_typedef, cast_mode)
|
||||
enforce(value_type.category != value_type.CATEGORY_ARRAY,
|
||||
'Multidimensional arrays are not allowed (protip: use nested types)')
|
||||
try:
|
||||
if size_spec.startswith('<='):
|
||||
max_size = int(size_spec[2:], 0)
|
||||
mode = ArrayType.MODE_DYNAMIC
|
||||
elif size_spec.startswith('<'):
|
||||
max_size = int(size_spec[1:], 0) - 1
|
||||
mode = ArrayType.MODE_DYNAMIC
|
||||
else:
|
||||
max_size = int(size_spec, 0)
|
||||
mode = ArrayType.MODE_STATIC
|
||||
except ValueError:
|
||||
error('Invalid array size specifier [%s] (valid patterns: [<=X], [<X], [X])', size_spec)
|
||||
enforce(max_size > 0, 'Array size must be positive, not %d', max_size)
|
||||
return ArrayType(value_type, mode, max_size)
|
||||
|
||||
def _parse_primitive_type(self, filename, base_name, bitlen, cast_mode):
|
||||
if cast_mode is None or cast_mode == 'saturated':
|
||||
cast_mode = PrimitiveType.CAST_MODE_SATURATED
|
||||
elif cast_mode == 'truncated':
|
||||
cast_mode = PrimitiveType.CAST_MODE_TRUNCATED
|
||||
else:
|
||||
error('Invalid cast mode [%s]', cast_mode)
|
||||
|
||||
if base_name == 'bool':
|
||||
return PrimitiveType(PrimitiveType.KIND_BOOLEAN, 1, cast_mode)
|
||||
try:
|
||||
kind = {
|
||||
'uint' : PrimitiveType.KIND_UNSIGNED_INT,
|
||||
'int' : PrimitiveType.KIND_SIGNED_INT,
|
||||
'float': PrimitiveType.KIND_FLOAT,
|
||||
}[base_name]
|
||||
except KeyError:
|
||||
error('Unknown primitive type (note: compound types should be in CamelCase)')
|
||||
|
||||
if kind == PrimitiveType.KIND_FLOAT:
|
||||
enforce(bitlen in (16, 32, 64), 'Invalid bit length for float type [%d]', bitlen)
|
||||
else:
|
||||
enforce(2 <= bitlen <= 64, 'Invalid bit length [%d] (note: use bool instead of uint1)', bitlen)
|
||||
return PrimitiveType(kind, bitlen, cast_mode)
|
||||
|
||||
def _parse_compound_type(self, filename, typedef):
|
||||
definition_filename = self._locate_compound_type_definition(filename, typedef)
|
||||
self.log.debug('Nested type [%s] is defined in [%s], parsing...', typedef, pretty_filename(definition_filename))
|
||||
t = self.parse(definition_filename)
|
||||
if t.kind == t.KIND_SERVICE:
|
||||
error('A service type can not be nested into another compound type')
|
||||
return t
|
||||
|
||||
def _parse_type(self, filename, typedef, cast_mode):
|
||||
typedef = typedef.strip()
|
||||
array_match = re.match(r'(.+?)\[([^\]]*)\]$', typedef)
|
||||
primitive_match = re.match(r'([a-z]+)(\d{1,2})$|(bool)$', typedef)
|
||||
|
||||
if array_match:
|
||||
assert not primitive_match
|
||||
value_typedef = array_match.group(1).strip()
|
||||
size_spec = array_match.group(2).strip()
|
||||
return self._parse_array_type(filename, value_typedef, size_spec, cast_mode)
|
||||
elif primitive_match:
|
||||
if primitive_match.group(0) == 'bool':
|
||||
return self._parse_primitive_type(filename, 'bool', 1, cast_mode)
|
||||
else:
|
||||
base_name = primitive_match.group(1)
|
||||
bitlen = int(primitive_match.group(2))
|
||||
return self._parse_primitive_type(filename, base_name, bitlen, cast_mode)
|
||||
else:
|
||||
enforce(cast_mode is None, 'Cast mode specifier is not applicable for compound types [%s]', cast_mode)
|
||||
return self._parse_compound_type(filename, typedef)
|
||||
|
||||
def _make_constant(self, attrtype, name, init_expression):
|
||||
enforce(attrtype.category == attrtype.CATEGORY_PRIMITIVE, 'Invalid type for constant')
|
||||
init_expression = ''.join(init_expression.split()) # Remove spaces
|
||||
value = evaluate_expression(init_expression)
|
||||
|
||||
if isinstance(value, str) and len(value) == 1: # ASCII character
|
||||
value = ord(value)
|
||||
elif isinstance(value, (float, int, bool, long)): # Numeric literal
|
||||
value = {
|
||||
attrtype.KIND_UNSIGNED_INT : long,
|
||||
attrtype.KIND_SIGNED_INT : long,
|
||||
attrtype.KIND_BOOLEAN : int, # Not bool because we need to check range
|
||||
attrtype.KIND_FLOAT : float
|
||||
}[attrtype.kind](value)
|
||||
else:
|
||||
error('Invalid type of constant initialization expression [%s]', type(value).__name__)
|
||||
|
||||
self.log.debug('Constant initialization expression evaluated as: [%s] --> %s', init_expression, repr(value))
|
||||
attrtype.validate_value_range(value)
|
||||
return Constant(attrtype, name, init_expression, value)
|
||||
|
||||
def _parse_line(self, filename, tokens):
|
||||
cast_mode = None
|
||||
if tokens[0] == 'saturated' or tokens[0] == 'truncated':
|
||||
cast_mode, tokens = tokens[0], tokens[1:]
|
||||
|
||||
if len(tokens) < 2:
|
||||
error('Invalid attribute definition')
|
||||
|
||||
typename, attrname, tokens = tokens[0], tokens[1], tokens[2:]
|
||||
validate_attribute_name(attrname)
|
||||
attrtype = self._parse_type(filename, typename, cast_mode)
|
||||
|
||||
if len(tokens) > 0:
|
||||
if len(tokens) < 2 or tokens[0] != '=':
|
||||
error('Constant assignment expected')
|
||||
expression = ' '.join(tokens[1:])
|
||||
return self._make_constant(attrtype, attrname, expression)
|
||||
else:
|
||||
return Field(attrtype, attrname)
|
||||
|
||||
def _tokenize(self, text):
|
||||
for idx, line in enumerate(text.splitlines()):
|
||||
line = re.sub('#.*', '', line).strip() # Remove comments and leading/trailing whitespaces
|
||||
if line:
|
||||
tokens = [tk for tk in line.split() if tk]
|
||||
yield idx + 1, tokens
|
||||
|
||||
def parse(self, filename):
|
||||
try:
|
||||
filename = os.path.abspath(filename)
|
||||
with open(filename) as f:
|
||||
source_text = f.read()
|
||||
|
||||
full_typename, default_dtid = self._full_typename_and_dtid_from_filename(filename)
|
||||
numbered_lines = list(self._tokenize(source_text))
|
||||
all_attributes_names = set()
|
||||
fields, constants, resp_fields, resp_constants = [], [], [], []
|
||||
response_part = False
|
||||
for num, tokens in numbered_lines:
|
||||
if tokens == ['---']:
|
||||
response_part = True
|
||||
all_attributes_names = set()
|
||||
continue
|
||||
try:
|
||||
attr = self._parse_line(filename, tokens)
|
||||
if attr.name in all_attributes_names:
|
||||
error('Duplicated attribute name [%s]', attr.name)
|
||||
all_attributes_names.add(attr.name)
|
||||
if isinstance(attr, Constant):
|
||||
(resp_constants if response_part else constants).append(attr)
|
||||
elif isinstance(attr, Field):
|
||||
(resp_fields if response_part else fields).append(attr)
|
||||
else:
|
||||
error('Unknown attribute type - internal error')
|
||||
except DsdlException as ex:
|
||||
if not ex.line:
|
||||
ex.line = num
|
||||
raise ex
|
||||
except Exception as ex:
|
||||
self.log.error('Internal error', exc_info=True)
|
||||
raise DsdlException('Internal error: %s' % str(ex), line=num)
|
||||
|
||||
if response_part:
|
||||
t = CompoundType(full_typename, CompoundType.KIND_SERVICE, filename, default_dtid, source_text)
|
||||
t.request_fields = fields
|
||||
t.request_constants = constants
|
||||
t.response_fields = resp_fields
|
||||
t.response_constants = resp_constants
|
||||
max_bitlen = t.get_max_bitlen_request(), t.get_max_bitlen_response()
|
||||
max_bytelen = tuple(map(bitlen_to_bytelen, max_bitlen))
|
||||
else:
|
||||
t = CompoundType(full_typename, CompoundType.KIND_MESSAGE, filename, default_dtid, source_text)
|
||||
t.fields = fields
|
||||
t.constants = constants
|
||||
max_bitlen = t.get_max_bitlen()
|
||||
max_bytelen = bitlen_to_bytelen(max_bitlen)
|
||||
|
||||
validate_data_type_id(t)
|
||||
validate_data_struct_len(t)
|
||||
self.log.info('Type [%s], default DTID: %s, signature: %08x, maxbits: %s, maxbytes: %s, DSSD:',
|
||||
full_typename, default_dtid, t.get_dsdl_signature(), max_bitlen, max_bytelen)
|
||||
for ln in t.get_dsdl_signature_source_definition().splitlines():
|
||||
self.log.info(' %s', ln)
|
||||
return t
|
||||
except DsdlException as ex:
|
||||
if not ex.file:
|
||||
ex.file = filename
|
||||
raise ex
|
||||
except IOError as ex:
|
||||
raise DsdlException('IO error: %s' % str(ex), file=filename)
|
||||
except Exception as ex:
|
||||
self.log.error('Internal error', exc_info=True)
|
||||
raise DsdlException('Internal error: %s' % str(ex), file=filename)
|
||||
|
||||
|
||||
def error(fmt, *args):
|
||||
raise DsdlException(fmt % args)
|
||||
|
||||
def enforce(cond, fmt, *args):
|
||||
if not cond:
|
||||
error(fmt, *args)
|
||||
|
||||
def bitlen_to_bytelen(x):
|
||||
return int((x + 7) / 8)
|
||||
|
||||
def evaluate_expression(expression):
|
||||
try:
|
||||
env = {
|
||||
'locals': None,
|
||||
'globals': None,
|
||||
'__builtins__': None,
|
||||
'true': 1,
|
||||
'false': 0
|
||||
}
|
||||
return eval(expression, env)
|
||||
except Exception as ex:
|
||||
error('Cannot evaluate expression: %s', str(ex))
|
||||
|
||||
def validate_search_directories(dirnames):
|
||||
dirnames = set(dirnames)
|
||||
dirnames = list(map(os.path.abspath, dirnames))
|
||||
for d1 in dirnames:
|
||||
for d2 in dirnames:
|
||||
if d1 == d2:
|
||||
continue
|
||||
enforce(not d1.startswith(d2), 'Nested search directories are not allowed [%s] [%s]', d1, d2)
|
||||
enforce(d1.split(os.path.sep)[-1] != d2.split(os.path.sep)[-1],
|
||||
'Namespace roots must be unique [%s] [%s]', d1, d2)
|
||||
return dirnames
|
||||
|
||||
def validate_namespace_name(name):
|
||||
for component in name.split('.'):
|
||||
enforce(re.match(r'[a-z][a-z0-9_]*$', component), 'Invalid namespace name [%s]', name)
|
||||
enforce(len(name) <= MAX_FULL_TYPE_NAME_LEN, 'Namespace name is too long [%s]', name)
|
||||
|
||||
def validate_compound_type_full_name(name):
|
||||
enforce('.' in name, 'Full type name must explicitly specify its namespace [%s]', name)
|
||||
short_name = name.split('.')[-1]
|
||||
namespace = '.'.join(name.split('.')[:-1])
|
||||
validate_namespace_name(namespace)
|
||||
enforce(re.match(r'[A-Z][A-Za-z0-9_]*$', short_name), 'Invalid type name [%s]', name)
|
||||
enforce(len(name) <= MAX_FULL_TYPE_NAME_LEN, 'Type name is too long [%s]', name)
|
||||
|
||||
def validate_attribute_name(name):
|
||||
enforce(re.match(r'[a-zA-Z][a-zA-Z0-9_]*$', name), 'Invalid attribute name [%s]', name)
|
||||
|
||||
def validate_data_type_id(t):
|
||||
if t.default_dtid is None:
|
||||
return
|
||||
if t.kind == t.KIND_MESSAGE:
|
||||
enforce(0 <= t.default_dtid <= MESSAGE_DATA_TYPE_ID_MAX,
|
||||
'Invalid data type ID for message [%s]', t.default_dtid)
|
||||
elif t.kind == t.KIND_SERVICE:
|
||||
enforce(0 <= t.default_dtid <= SERVICE_DATA_TYPE_ID_MAX,
|
||||
'Invalid data type ID for service [%s]', t.default_dtid)
|
||||
else:
|
||||
error('Invalid kind: %s', t.kind)
|
||||
|
||||
def validate_data_struct_len(t):
|
||||
enforce(t.category == t.CATEGORY_COMPOUND, 'Data structure length can be enforced only for compound types')
|
||||
# EXtracting sizes
|
||||
if t.kind == t.KIND_MESSAGE:
|
||||
bitlens = [t.get_max_bitlen()]
|
||||
elif t.kind == t.KIND_SERVICE:
|
||||
bitlens = t.get_max_bitlen_request(), t.get_max_bitlen_response()
|
||||
# Detecting the limit
|
||||
if t.kind == t.KIND_MESSAGE and t.default_dtid is not None:
|
||||
max_bytes = MAX_MESSAGE_STRUCT_LEN_BYTES
|
||||
else:
|
||||
max_bytes = MAX_SERVICE_STRUCT_LEN_BYTES
|
||||
# Checking
|
||||
for bitlen in bitlens:
|
||||
bytelen = bitlen_to_bytelen(bitlen)
|
||||
enforce(0 <= bytelen <= max_bytes,
|
||||
'Max data structure length is invalid: %d bits, %d bytes; limit %d bytes', bitlen, bytelen, max_bytes)
|
||||
|
||||
|
||||
def parse_namespaces(source_dirs, search_dirs=None):
|
||||
'''
|
||||
Use only this function to parse DSDL definitions.
|
||||
This function takes a list of root namespace directories (containing DSDL definition files to parse) and an
|
||||
optional list of search directories (containing DSDL definition files that can be referenced from the types
|
||||
that are going to be parsed).
|
||||
|
||||
Returns the list of parsed type definitions, where type of each element is CompoundType.
|
||||
|
||||
Args:
|
||||
source_dirs List of root namespace directories to parse.
|
||||
search_dirs List of root namespace directories with referenced types (optional). This list is
|
||||
automaitcally extended with source_dirs.
|
||||
Example:
|
||||
>>> import pyuavcan
|
||||
>>> a = pyuavcan.dsdl.parse_namespaces(['../dsdl/uavcan'])
|
||||
>>> len(a)
|
||||
77
|
||||
>>> a[0]
|
||||
uavcan.Timestamp
|
||||
>>> a[0].fields
|
||||
[truncated uint48 husec]
|
||||
>>> a[0].constants
|
||||
[saturated uint48 UNKNOWN = 0, saturated uint48 USEC_PER_LSB = 100]
|
||||
'''
|
||||
def walk():
|
||||
import fnmatch
|
||||
from functools import partial
|
||||
def on_walk_error(directory, ex):
|
||||
raise DsdlException('OS error in [%s]: %s' % (directory, str(ex)))
|
||||
for source_dir in source_dirs:
|
||||
walker = os.walk(source_dir, onerror=partial(on_walk_error, source_dir), followlinks=True)
|
||||
for root, _dirnames, filenames in walker:
|
||||
for filename in fnmatch.filter(filenames, '*.uavcan'):
|
||||
filename = os.path.join(root, filename)
|
||||
yield filename
|
||||
|
||||
all_default_dtid = {} # (kind, dtid) : filename
|
||||
def ensure_unique_dtid(t, filename):
|
||||
if t.default_dtid is None:
|
||||
return
|
||||
key = t.kind, t.default_dtid
|
||||
if key in all_default_dtid:
|
||||
first = pretty_filename(all_default_dtid[key])
|
||||
second = pretty_filename(filename)
|
||||
error('Default data type ID collision: [%s] [%s]', first, second)
|
||||
all_default_dtid[key] = filename
|
||||
|
||||
parser = Parser(source_dirs + (search_dirs or []))
|
||||
output_types = []
|
||||
for filename in walk():
|
||||
t = parser.parse(filename)
|
||||
ensure_unique_dtid(t, filename)
|
||||
output_types.append(t)
|
||||
return output_types
|
||||
@@ -1,71 +0,0 @@
|
||||
#
|
||||
# UAVCAN DSDL signature computation
|
||||
#
|
||||
# Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
#
|
||||
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
|
||||
#
|
||||
# CRC-64-WE
|
||||
# Description: http://reveng.sourceforge.net/crc-catalogue/17plus.htm#crc.cat-bits.64
|
||||
# Initial value: 0xFFFFFFFFFFFFFFFF
|
||||
# Poly: 0x42F0E1EBA9EA3693
|
||||
# Reverse: no
|
||||
# Output xor: 0xFFFFFFFFFFFFFFFF
|
||||
# Check: 0x62EC59E3F1A4F00A
|
||||
#
|
||||
class Signature:
|
||||
'''
|
||||
This class implements the UAVCAN DSDL signature hash function. Please refer to the specification for details.
|
||||
'''
|
||||
MASK64 = 0xFFFFFFFFFFFFFFFF
|
||||
POLY = 0x42F0E1EBA9EA3693
|
||||
|
||||
def __init__(self, extend_from=None):
|
||||
'''
|
||||
extend_from Initial value (optional)
|
||||
'''
|
||||
if extend_from is not None:
|
||||
self._crc = (int(extend_from) & Signature.MASK64) ^ Signature.MASK64
|
||||
else:
|
||||
self._crc = Signature.MASK64
|
||||
|
||||
def add(self, data_bytes):
|
||||
'''Feed ASCII string or bytes to the signature function'''
|
||||
try:
|
||||
if isinstance(data_bytes, basestring): # Python 2.7 compatibility
|
||||
data_bytes = map(ord, data_bytes)
|
||||
except NameError:
|
||||
if isinstance(data_bytes, str): # This branch will be taken on Python 3
|
||||
data_bytes = map(ord, data_bytes)
|
||||
|
||||
for b in data_bytes:
|
||||
self._crc ^= (b << 56) & Signature.MASK64
|
||||
for _ in range(8):
|
||||
if self._crc & (1 << 63):
|
||||
self._crc = ((self._crc << 1) & Signature.MASK64) ^ Signature.POLY
|
||||
else:
|
||||
self._crc <<= 1
|
||||
|
||||
def get_value(self):
|
||||
'''Returns integer signature value'''
|
||||
return (self._crc & Signature.MASK64) ^ Signature.MASK64
|
||||
|
||||
|
||||
def compute_signature(data):
|
||||
'''
|
||||
One-shot signature computation for ASCII string or bytes.
|
||||
Returns integer signture value.
|
||||
'''
|
||||
s = Signature()
|
||||
s.add(data)
|
||||
return s.get_value()
|
||||
|
||||
|
||||
# if __name__ == '__main__':
|
||||
if 1:
|
||||
s = Signature()
|
||||
s.add(b'123')
|
||||
s.add('456789')
|
||||
assert s.get_value() == 0x62EC59E3F1A4F00A
|
||||
@@ -1,28 +0,0 @@
|
||||
#
|
||||
# UAVCAN DSDL type range limits
|
||||
#
|
||||
# Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
#
|
||||
|
||||
from __future__ import division, absolute_import, print_function, unicode_literals
|
||||
from .common import DsdlException
|
||||
|
||||
def get_unsigned_integer_range(bitlen):
|
||||
if not 1 <= bitlen <= 64:
|
||||
raise DsdlException('Invalid bit length for integer type: %d' % bitlen)
|
||||
return 0, (1 << bitlen) - 1
|
||||
|
||||
def get_signed_integer_range(bitlen):
|
||||
_, uint_max = get_unsigned_integer_range(bitlen)
|
||||
return -int(uint_max / 2) - 1, int(uint_max / 2)
|
||||
|
||||
def get_float_range(bitlen):
|
||||
try:
|
||||
maxvalue = {
|
||||
16: 65504.0,
|
||||
32: 3.40282346638528859812e+38,
|
||||
64: 1.79769313486231570815e+308
|
||||
}[bitlen]
|
||||
except KeyError:
|
||||
raise DsdlException('Invalid bit length for float type: %d' % bitlen)
|
||||
return -maxvalue, maxvalue
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from distutils.core import setup
|
||||
|
||||
args = dict(
|
||||
name='pyuavcan',
|
||||
version='0.1',
|
||||
description='UAVCAN for Python',
|
||||
packages=['pyuavcan', 'pyuavcan.dsdl'],
|
||||
author='Pavel Kirienko',
|
||||
author_email='pavel.kirienko@gmail.com',
|
||||
url='http://uavcan.org',
|
||||
license='MIT'
|
||||
)
|
||||
|
||||
setup(**args)
|
||||
Reference in New Issue
Block a user