mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-29 02:13:33 +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