Pyuavcan and DSDL removed, will be returned back as submodules in the next commit

This commit is contained in:
Pavel Kirienko
2015-06-10 14:59:30 +03:00
parent da69feec76
commit 03c251db4f
82 changed files with 0 additions and 2054 deletions

View File

@@ -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.* | |

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,6 +0,0 @@
#
# Magnetic field readings in body frame.
#
float16[3] magnetic_field
float16[<=9] magnetic_field_covariance

View File

@@ -1,6 +0,0 @@
#
# TAS.
#
float16 true_airspeed # m/s
float16 true_airspeed_variance # (m/s)^2

View File

@@ -1,6 +0,0 @@
#
# IAS.
#
float16 indicated_airspeed # m/s
float16 indicated_airspeed_variance # (m/s)^2

View File

@@ -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

View File

@@ -1,7 +0,0 @@
#
# Body sideslip in radians.
# Yaw right: +, yaw left: -
#
float16 sideslip_angle # Radians
float16 sideslip_angle_variance # Radians^2

View File

@@ -1,6 +0,0 @@
#
# Static pressure.
#
float32 static_pressure # Pascal
float16 static_pressure_variance # Pascal^2

View File

@@ -1,6 +0,0 @@
#
# Static temperature.
#
float16 static_temperature # Kelvin
float16 static_temperature_variance # Kelvin^2

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,6 +0,0 @@
#
# Simple RPM setpoint.
# Negative values indicate reverse rotation.
#
int18[<=20] rpm

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,8 +0,0 @@
#
# Generic cargo holder/hardpoint command.
#
uint8 hardpoint_id
# Either a binary command (0 - release, 1+ - hold) or bitmask
uint16 command

View File

@@ -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

View File

@@ -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

View File

@@ -1,5 +0,0 @@
#
# Lights control command.
#
SingleLightCommand[<32] commands

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,10 +0,0 @@
#
# Encapsulated MAVLink message.
#
uint8 seq
uint8 sysid
uint8 compid
uint8 msgid
uint8[<=105] payload

View File

@@ -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

View File

@@ -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.
#

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,7 +0,0 @@
#
# Single CAN iface statistics
#
uint48 frames_tx
uint48 frames_rx
uint48 errors

View File

@@ -1,7 +0,0 @@
#
# Data type kind (message or service).
#
uint8 SERVICE = 0
uint8 MESSAGE = 1
uint8 value

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,7 +0,0 @@
#
# Generic log message.
#
LogLevel level
uint8[<32] source
uint8[<94] text

View File

@@ -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

View File

@@ -1,9 +0,0 @@
#
# Log message severity
#
uint3 DEBUG = 0
uint3 INFO = 1
uint3 WARNING = 2
uint3 ERROR = 3
uint3 value

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,9 +0,0 @@
#
# Nested type.
# File system path in UTF8.
# The only valid separator is forward slash.
#
uint8 SEPARATOR = '/'
uint8[<=200] path

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,5 +0,0 @@
#
# This type is nested in Value.
#
uint8[<=127] value

View File

@@ -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

View File

@@ -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

View File

@@ -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']

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)