AP_UAVCAN: move libuavcan to libcanard driver

This commit is contained in:
bugobliterator 2023-01-05 12:09:23 +11:00 committed by Andrew Tridgell
parent cb628e6875
commit 7067e9d917
13 changed files with 920 additions and 1406 deletions

View File

@ -0,0 +1,309 @@
#include "AP_Canard_iface.h"
#include <AP_HAL/AP_HAL.h>
#include <AP_CANManager/AP_CANManager.h>
#if HAL_ENABLE_LIBUAVCAN_DRIVERS
#include <canard/handler_list.h>
#include <canard/transfer_object.h>
extern const AP_HAL::HAL& hal;
#define LOG_TAG "DroneCANIface"
#define DEBUG_PKTS 0
DEFINE_HANDLER_LIST_HEADS();
DEFINE_TRANSFER_OBJECT_HEADS();
#if AP_TEST_DRONECAN_DRIVERS
CanardInterface* CanardInterface::canard_ifaces[] = {nullptr, nullptr, nullptr};
CanardInterface CanardInterface::test_iface{2};
uint8_t test_node_mem_area[1024];
HAL_Semaphore test_iface_sem;
#endif
CanardInterface::CanardInterface(uint8_t iface_index) :
Interface(iface_index) {
#if AP_TEST_DRONECAN_DRIVERS
if (iface_index < 3) {
canard_ifaces[iface_index] = this;
}
if (iface_index == 0) {
test_iface.init(test_node_mem_area, sizeof(test_node_mem_area), 125);
}
#endif
}
void CanardInterface::init(void* mem_arena, size_t mem_arena_size, uint8_t node_id) {
canardInit(&canard, mem_arena, mem_arena_size, onTransferReception, shouldAcceptTransfer, this);
canardSetLocalNodeID(&canard, node_id);
initialized = true;
}
bool CanardInterface::broadcast(const Canard::Transfer &bcast_transfer) {
if (!initialized) {
return false;
}
WITH_SEMAPHORE(_sem);
#if AP_TEST_DRONECAN_DRIVERS
if (this == &test_iface) {
test_iface_sem.take_blocking();
}
#endif
// do canard broadcast
bool success = canardBroadcast(&canard,
bcast_transfer.data_type_signature,
bcast_transfer.data_type_id,
bcast_transfer.inout_transfer_id,
bcast_transfer.priority,
bcast_transfer.payload,
bcast_transfer.payload_len,
AP_HAL::native_micros64() + (bcast_transfer.timeout_ms * 1000)
#if CANARD_MULTI_IFACE
, ((1<<num_ifaces) - 1) // send over all ifaces
#endif
#if HAL_CANFD_SUPPORTED
, bcast_transfer.canfd
#endif
) > 0;
#if AP_TEST_DRONECAN_DRIVERS
if (this == &test_iface) {
test_iface_sem.give();
}
#endif
return success;
}
bool CanardInterface::request(uint8_t destination_node_id, const Canard::Transfer &req_transfer) {
if (!initialized) {
return false;
}
WITH_SEMAPHORE(_sem);
// do canard request
return canardRequestOrRespond(&canard,
destination_node_id,
req_transfer.data_type_signature,
req_transfer.data_type_id,
req_transfer.inout_transfer_id,
req_transfer.priority,
CanardRequest,
req_transfer.payload,
req_transfer.payload_len,
AP_HAL::native_micros64() + (req_transfer.timeout_ms * 1000)
#if CANARD_MULTI_IFACE
, ((1<<num_ifaces) - 1) // send over all ifaces
#endif
#if HAL_CANFD_SUPPORTED
, false
#endif
) > 0;
}
bool CanardInterface::respond(uint8_t destination_node_id, const Canard::Transfer &res_transfer) {
if (!initialized) {
return false;
}
WITH_SEMAPHORE(_sem);
// do canard respond
return canardRequestOrRespond(&canard,
destination_node_id,
res_transfer.data_type_signature,
res_transfer.data_type_id,
res_transfer.inout_transfer_id,
res_transfer.priority,
CanardResponse,
res_transfer.payload,
res_transfer.payload_len,
AP_HAL::native_micros64() + (res_transfer.timeout_ms * 1000)
#if CANARD_MULTI_IFACE
, ((1<<num_ifaces) - 1) // send over all ifaces
#endif
#if HAL_CANFD_SUPPORTED
, false
#endif
) > 0;
}
void CanardInterface::onTransferReception(CanardInstance* ins, CanardRxTransfer* transfer) {
CanardInterface* iface = (CanardInterface*) ins->user_reference;
iface->handle_message(*transfer);
}
bool CanardInterface::shouldAcceptTransfer(const CanardInstance* ins,
uint64_t* out_data_type_signature,
uint16_t data_type_id,
CanardTransferType transfer_type,
uint8_t source_node_id) {
CanardInterface* iface = (CanardInterface*) ins->user_reference;
return iface->accept_message(data_type_id, *out_data_type_signature);
}
#if AP_TEST_DRONECAN_DRIVERS
void CanardInterface::processTestRx() {
if (!test_iface.initialized) {
return;
}
WITH_SEMAPHORE(test_iface_sem);
for (const CanardCANFrame* txf = canardPeekTxQueue(&test_iface.canard); txf != NULL; txf = canardPeekTxQueue(&test_iface.canard)) {
if (canard_ifaces[0]) {
canardHandleRxFrame(&canard_ifaces[0]->canard, txf, AP_HAL::native_micros64());
}
canardPopTxQueue(&test_iface.canard);
}
}
#endif
void CanardInterface::processTx() {
WITH_SEMAPHORE(_sem);
for (uint8_t iface = 0; iface < num_ifaces; iface++) {
if (ifaces[iface] == NULL) {
continue;
}
auto txq = canard.tx_queue;
if (txq == nullptr) {
return;
}
AP_HAL::CANFrame txmsg {};
// scan through list of pending transfers
while (true) {
auto txf = &txq->frame;
txmsg.dlc = AP_HAL::CANFrame::dataLengthToDlc(txf->data_len);
memcpy(txmsg.data, txf->data, txf->data_len);
txmsg.id = (txf->id | AP_HAL::CANFrame::FlagEFF);
#if HAL_CANFD_SUPPORTED
txmsg.canfd = txf->canfd;
#endif
bool write = true;
bool read = false;
ifaces[iface]->select(read, write, &txmsg, 0);
if ((AP_HAL::native_micros64() < txf->deadline_usec) && (txf->iface_mask & (1U<<iface)) && write) {
// try sending to interfaces, clearing the mask if we succeed
if (ifaces[iface]->send(txmsg, txf->deadline_usec, 0) > 0) {
txf->iface_mask &= ~(1U<<iface);
} else {
// if we fail to send then we try sending on next interface
break;
}
}
// look at next transfer
txq = txq->next;
if (txq == nullptr) {
break;
}
}
}
// purge expired transfers
for (const CanardCANFrame* txf = canardPeekTxQueue(&canard); txf != NULL; txf = canardPeekTxQueue(&canard)) {
if ((AP_HAL::native_micros64() >= txf->deadline_usec) || (txf->iface_mask == 0)) {
canardPopTxQueue(&canard);
} else {
break;
}
}
}
void CanardInterface::processRx() {
AP_HAL::CANFrame rxmsg;
for (uint8_t i=0; i<num_ifaces; i++) {
while(true) {
if (ifaces[i] == NULL) {
break;
}
bool read_select = true;
bool write_select = false;
ifaces[i]->select(read_select, write_select, nullptr, 0);
if (!read_select) { // No data pending
break;
}
CanardCANFrame rx_frame {};
//palToggleLine(HAL_GPIO_PIN_LED);
uint64_t timestamp;
AP_HAL::CANIface::CanIOFlags flags;
if (ifaces[i]->receive(rxmsg, timestamp, flags) <= 0) {
break;
}
rx_frame.data_len = AP_HAL::CANFrame::dlcToDataLength(rxmsg.dlc);
memcpy(rx_frame.data, rxmsg.data, rx_frame.data_len);
#if HAL_CANFD_SUPPORTED
rx_frame.canfd = rxmsg.canfd;
#endif
rx_frame.id = rxmsg.id;
#if CANARD_MULTI_IFACE
rx_frame.iface_id = i;
#endif
{
WITH_SEMAPHORE(_sem);
#if DEBUG_PKTS
const int16_t res =
#endif
canardHandleRxFrame(&canard, &rx_frame, timestamp);
#if DEBUG_PKTS
// hal.console->printf("DTID: %u\n", extractDataType(rx_frame.id));
// hal.console->printf("Rx %d, IF%d %lx: ", res, i, rx_frame.id);
if (res < 0 &&
res != -CANARD_ERROR_RX_NOT_WANTED &&
res != -CANARD_ERROR_RX_WRONG_ADDRESS) {
hal.console->printf("Rx error %d, IF%d %lx: \n", res, i, rx_frame.id);
// for (uint8_t index = 0; index < rx_frame.data_len; index++) {
// hal.console->printf("%02x", rx_frame.data[index]);
// }
// hal.console->printf("\n");
}
#endif
}
}
}
}
void CanardInterface::process(uint32_t duration_ms) {
#if AP_TEST_DRONECAN_DRIVERS
const uint64_t deadline = AP_HAL::micros64() + duration_ms*1000;
while (AP_HAL::micros64() < deadline) {
processTestRx();
hal.scheduler->delay_microseconds(1000);
}
#else
const uint64_t deadline = AP_HAL::native_micros64() + duration_ms*1000;
while (true) {
processRx();
processTx();
uint64_t now = AP_HAL::native_micros64();
if (now < deadline) {
_event_handle.wait(deadline - now);
} else {
break;
}
}
#endif
}
bool CanardInterface::add_interface(AP_HAL::CANIface *can_iface)
{
if (num_ifaces > HAL_NUM_CAN_IFACES) {
AP::can().log_text(AP_CANManager::LOG_ERROR, LOG_TAG, "DroneCANIfaceMgr: Num Ifaces Exceeded\n");
return false;
}
if (can_iface == nullptr) {
AP::can().log_text(AP_CANManager::LOG_ERROR, LOG_TAG, "DroneCANIfaceMgr: Iface Null\n");
return false;
}
if (ifaces[num_ifaces] != nullptr) {
AP::can().log_text(AP_CANManager::LOG_ERROR, LOG_TAG, "DroneCANIfaceMgr: Iface already added\n");
return false;
}
ifaces[num_ifaces] = can_iface;
if (ifaces[num_ifaces] == nullptr) {
AP::can().log_text(AP_CANManager::LOG_ERROR, LOG_TAG, "DroneCANIfaceMgr: Can't alloc uavcan::iface\n");
return false;
}
if (!can_iface->set_event_handle(&_event_handle)) {
AP::can().log_text(AP_CANManager::LOG_ERROR, LOG_TAG, "DroneCANIfaceMgr: Setting event handle failed\n");
return false;
}
AP::can().log_text(AP_CANManager::LOG_INFO, LOG_TAG, "DroneCANIfaceMgr: Successfully added interface %d\n", int(num_ifaces));
num_ifaces++;
return true;
}
#endif // #if HAL_ENABLE_LIBUAVCAN_DRIVERS

View File

@ -0,0 +1,68 @@
#pragma once
#include <AP_HAL/AP_HAL.h>
#if HAL_ENABLE_LIBUAVCAN_DRIVERS
#include <canard/interface.h>
class AP_UAVCAN;
class CanardInterface : public Canard::Interface {
friend class AP_UAVCAN;
public:
/// @brief delete copy constructor and assignment operator
CanardInterface(const CanardInterface&) = delete;
CanardInterface& operator=(const CanardInterface&) = delete;
CanardInterface(uint8_t driver_index);
void init(void* mem_arena, size_t mem_arena_size, uint8_t node_id);
/// @brief broadcast message to all listeners on Interface
/// @param bc_transfer
/// @return true if message was added to the queue
bool broadcast(const Canard::Transfer &bcast_transfer) override;
/// @brief request message from
/// @param destination_node_id
/// @param req_transfer
/// @return true if request was added to the queue
bool request(uint8_t destination_node_id, const Canard::Transfer &req_transfer) override;
/// @brief respond to a request
/// @param destination_node_id
/// @param res_transfer
/// @return true if response was added to the queue
bool respond(uint8_t destination_node_id, const Canard::Transfer &res_transfer) override;
void processTx();
void processRx();
void process(uint32_t duration);
static void onTransferReception(CanardInstance* ins, CanardRxTransfer* transfer);
static bool shouldAcceptTransfer(const CanardInstance* ins,
uint64_t* out_data_type_signature,
uint16_t data_type_id,
CanardTransferType transfer_type,
uint8_t source_node_id);
bool add_interface(AP_HAL::CANIface *can_drv);
#if AP_TEST_DRONECAN_DRIVERS
static CanardInterface& get_test_iface() { return test_iface; }
static void processTestRx();
#endif
uint8_t get_node_id() const override { return canard.node_id; }
private:
CanardInstance canard;
AP_HAL::CANIface* ifaces[HAL_NUM_CAN_IFACES];
#if AP_TEST_DRONECAN_DRIVERS
static CanardInterface* canard_ifaces[3];
static CanardInterface test_iface;
#endif
uint8_t num_ifaces;
HAL_EventHandle _event_handle;
bool initialized;
HAL_Semaphore _sem;
};
#endif // HAL_ENABLE_LIBUAVCAN_DRIVERS

File diff suppressed because it is too large Load Diff

View File

@ -20,17 +20,20 @@
#if HAL_ENABLE_LIBUAVCAN_DRIVERS #if HAL_ENABLE_LIBUAVCAN_DRIVERS
#include <uavcan/uavcan.hpp> #include "AP_Canard_iface.h"
#include "AP_UAVCAN_IfaceMgr.h"
#include "AP_UAVCAN_Clock.h"
#include <AP_CANManager/AP_CANManager.h> #include <AP_CANManager/AP_CANManager.h>
#include <AP_HAL/Semaphores.h> #include <AP_HAL/Semaphores.h>
#include <AP_Param/AP_Param.h> #include <AP_Param/AP_Param.h>
#include <AP_ESC_Telem/AP_ESC_Telem_Backend.h> #include <AP_ESC_Telem/AP_ESC_Telem_Backend.h>
#include <uavcan/protocol/param/GetSet.hpp>
#include <uavcan/protocol/param/ExecuteOpcode.hpp>
#include <SRV_Channel/SRV_Channel_config.h> #include <SRV_Channel/SRV_Channel_config.h>
#include <canard/publisher.h>
#include <canard/subscriber.h>
#include <canard/service_client.h>
#include <canard/service_server.h>
#include <stdio.h>
#include "AP_UAVCAN_DNA_Server.h"
#include <canard.h>
#include <dronecan_msgs.h>
#ifndef UAVCAN_SRV_NUMBER #ifndef UAVCAN_SRV_NUMBER
#define UAVCAN_SRV_NUMBER NUM_SERVO_CHANNELS #define UAVCAN_SRV_NUMBER NUM_SERVO_CHANNELS
@ -49,63 +52,12 @@
#define AP_UAVCAN_MAX_LED_DEVICES 4 #define AP_UAVCAN_MAX_LED_DEVICES 4
// fwd-declare callback classes // fwd-declare callback classes
class ButtonCb;
class TrafficReportCb;
class ActuatorStatusCb;
class ActuatorStatusVolzCb;
class ESCStatusCb;
class DebugCb;
class ParamGetSetCb;
class ParamExecuteOpcodeCb;
class AP_PoolAllocator;
class AP_UAVCAN_DNA_Server; class AP_UAVCAN_DNA_Server;
#if defined(__GNUC__) && (__GNUC__ > 8)
#define DISABLE_W_CAST_FUNCTION_TYPE_PUSH \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wcast-function-type\"")
#define DISABLE_W_CAST_FUNCTION_TYPE_POP \
_Pragma("GCC diagnostic pop")
#else
#define DISABLE_W_CAST_FUNCTION_TYPE_PUSH
#define DISABLE_W_CAST_FUNCTION_TYPE_POP
#endif
#if defined(__GNUC__) && (__GNUC__ >= 11)
#define DISABLE_W_CAST_FUNCTION_TYPE_WITH_VOID (void*)
#else
#define DISABLE_W_CAST_FUNCTION_TYPE_WITH_VOID
#endif
/*
Frontend Backend-Registry Binder: Whenever a message of said DataType_ from new node is received,
the Callback will invoke registry to register the node as separate backend.
*/
#define UC_REGISTRY_BINDER(ClassName_, DataType_) \
class ClassName_ : public AP_UAVCAN::RegistryBinder<DataType_> { \
typedef void (*CN_Registry)(AP_UAVCAN*, uint8_t, const ClassName_&); \
public: \
ClassName_() : RegistryBinder() {} \
DISABLE_W_CAST_FUNCTION_TYPE_PUSH \
ClassName_(AP_UAVCAN* uc, CN_Registry ffunc) : \
RegistryBinder(uc, (Registry)DISABLE_W_CAST_FUNCTION_TYPE_WITH_VOID ffunc) {} \
DISABLE_W_CAST_FUNCTION_TYPE_POP \
}
#define UC_CLIENT_CALL_REGISTRY_BINDER(ClassName_, DataType_) \
class ClassName_ : public AP_UAVCAN::ClientCallRegistryBinder<DataType_> { \
typedef void (*CN_Registry)(AP_UAVCAN*, uint8_t, const ClassName_&); \
public: \
ClassName_() : ClientCallRegistryBinder() {} \
DISABLE_W_CAST_FUNCTION_TYPE_PUSH \
ClassName_(AP_UAVCAN* uc, CN_Registry ffunc) : \
ClientCallRegistryBinder(uc, (ClientCallRegistry)DISABLE_W_CAST_FUNCTION_TYPE_WITH_VOID ffunc) {} \
DISABLE_W_CAST_FUNCTION_TYPE_POP \
}
class AP_UAVCAN : public AP_CANDriver, public AP_ESC_Telem_Backend { class AP_UAVCAN : public AP_CANDriver, public AP_ESC_Telem_Backend {
friend class AP_UAVCAN_DNA_Server; friend class AP_UAVCAN_DNA_Server;
public: public:
AP_UAVCAN(); AP_UAVCAN(const int driver_index);
~AP_UAVCAN(); ~AP_UAVCAN();
static const struct AP_Param::GroupInfo var_info[]; static const struct AP_Param::GroupInfo var_info[];
@ -117,13 +69,14 @@ public:
void init(uint8_t driver_index, bool enable_filters) override; void init(uint8_t driver_index, bool enable_filters) override;
bool add_interface(AP_HAL::CANIface* can_iface) override; bool add_interface(AP_HAL::CANIface* can_iface) override;
uavcan::Node<0>* get_node() { return _node; }
uint8_t get_driver_index() const { return _driver_index; } uint8_t get_driver_index() const { return _driver_index; }
FUNCTOR_TYPEDEF(ParamGetSetIntCb, bool, AP_UAVCAN*, const uint8_t, const char*, int32_t &); FUNCTOR_TYPEDEF(ParamGetSetIntCb, bool, AP_UAVCAN*, const uint8_t, const char*, int32_t &);
FUNCTOR_TYPEDEF(ParamGetSetFloatCb, bool, AP_UAVCAN*, const uint8_t, const char*, float &); FUNCTOR_TYPEDEF(ParamGetSetFloatCb, bool, AP_UAVCAN*, const uint8_t, const char*, float &);
FUNCTOR_TYPEDEF(ParamSaveCb, void, AP_UAVCAN*, const uint8_t, bool); FUNCTOR_TYPEDEF(ParamSaveCb, void, AP_UAVCAN*, const uint8_t, bool);
void send_node_status();
///// SRV output ///// ///// SRV output /////
void SRV_push_servos(void); void SRV_push_servos(void);
@ -138,7 +91,7 @@ public:
// Send Reboot command // Send Reboot command
// Note: Do not call this from outside UAVCAN thread context, // Note: Do not call this from outside UAVCAN thread context,
// you can call this from uavcan callbacks and handlers. // you can call this from dronecan callbacks and handlers.
// THIS IS NOT A THREAD SAFE API! // THIS IS NOT A THREAD SAFE API!
void send_reboot_request(uint8_t node_id); void send_reboot_request(uint8_t node_id);
@ -151,57 +104,6 @@ public:
// Save parameters // Save parameters
bool save_parameters_on_node(uint8_t node_id, ParamSaveCb *cb); bool save_parameters_on_node(uint8_t node_id, ParamSaveCb *cb);
template <typename DataType_>
class RegistryBinder {
protected:
typedef void (*Registry)(AP_UAVCAN* _ap_uavcan, uint8_t _node_id, const RegistryBinder& _cb);
AP_UAVCAN* _uc;
Registry _ffunc;
public:
RegistryBinder() :
_uc(),
_ffunc(),
msg() {}
RegistryBinder(AP_UAVCAN* uc, Registry ffunc) :
_uc(uc),
_ffunc(ffunc),
msg(nullptr) {}
void operator()(const uavcan::ReceivedDataStructure<DataType_>& _msg) {
msg = &_msg;
_ffunc(_uc, _msg.getSrcNodeID().get(), *this);
}
const uavcan::ReceivedDataStructure<DataType_> *msg;
};
// ClientCallRegistryBinder
template <typename DataType_>
class ClientCallRegistryBinder {
protected:
typedef void (*ClientCallRegistry)(AP_UAVCAN* _ap_uavcan, uint8_t _node_id, const ClientCallRegistryBinder& _cb);
AP_UAVCAN* _uc;
ClientCallRegistry _ffunc;
public:
ClientCallRegistryBinder() :
_uc(),
_ffunc(),
rsp() {}
ClientCallRegistryBinder(AP_UAVCAN* uc, ClientCallRegistry ffunc) :
_uc(uc),
_ffunc(ffunc),
rsp(nullptr) {}
void operator()(const uavcan::ServiceCallResult<DataType_>& _rsp) {
rsp = &_rsp;
_ffunc(_uc, _rsp.getCallID().server_node_id.get(), *this);
}
const uavcan::ServiceCallResult<DataType_> *rsp;
};
// options bitmask // options bitmask
enum class Options : uint16_t { enum class Options : uint16_t {
DNA_CLEAR_DATABASE = (1U<<0), DNA_CLEAR_DATABASE = (1U<<0),
@ -221,9 +123,7 @@ public:
// 0. return true if it was set // 0. return true if it was set
bool check_and_reset_option(Options option); bool check_and_reset_option(Options option);
// This will be needed to implement if UAVCAN is used with multithreading CanardInterface& get_canard_iface() { return canard_iface; }
// Such cases will be firmware update, etc.
class RaiiSynchronizer {};
private: private:
void loop(void); void loop(void);
@ -270,7 +170,7 @@ private:
uint8_t param_save_request_node_id; uint8_t param_save_request_node_id;
// UAVCAN parameters // UAVCAN parameters
AP_Int8 _uavcan_node; AP_Int8 _dronecan_node;
AP_Int32 _servo_bm; AP_Int32 _servo_bm;
AP_Int32 _esc_bm; AP_Int32 _esc_bm;
AP_Int8 _esc_offset; AP_Int8 _esc_offset;
@ -279,14 +179,12 @@ private:
AP_Int16 _notify_state_hz; AP_Int16 _notify_state_hz;
AP_Int16 _pool_size; AP_Int16 _pool_size;
AP_PoolAllocator *_allocator; uint32_t *mem_pool;
AP_UAVCAN_DNA_Server *_dna_server;
uavcan::Node<0> *_node; AP_UAVCAN_DNA_Server _dna_server;
uint8_t _driver_index; uint8_t _driver_index;
uavcan::CanIfaceMgr* _iface_mgr;
char _thread_name[13]; char _thread_name[13];
bool _initialized; bool _initialized;
///// SRV output ///// ///// SRV output /////
@ -355,22 +253,80 @@ private:
static HAL_Semaphore _telem_sem; static HAL_Semaphore _telem_sem;
// node status send
uint32_t _node_status_last_send_ms;
// safety status send state // safety status send state
uint32_t _last_safety_state_ms; uint32_t _last_safety_state_ms;
// notify vehicle state // notify vehicle state
uint32_t _last_notify_state_ms; uint32_t _last_notify_state_ms;
uavcan_protocol_NodeStatus node_status_msg;
CanardInterface canard_iface;
Canard::Publisher<uavcan_protocol_NodeStatus> node_status{canard_iface};
Canard::Publisher<uavcan_equipment_actuator_ArrayCommand> act_out_array{canard_iface};
Canard::Publisher<uavcan_equipment_esc_RawCommand> esc_raw{canard_iface};
Canard::Publisher<uavcan_equipment_indication_LightsCommand> rgb_led{canard_iface};
Canard::Publisher<uavcan_equipment_indication_BeepCommand> buzzer{canard_iface};
Canard::Publisher<ardupilot_indication_SafetyState> safety_state{canard_iface};
Canard::Publisher<uavcan_equipment_safety_ArmingStatus> arming_status{canard_iface};
Canard::Publisher<uavcan_equipment_gnss_RTCMStream> rtcm_stream{canard_iface};
Canard::Publisher<ardupilot_indication_NotifyState> notify_state{canard_iface};
#if AP_DRONECAN_SEND_GPS
Canard::Publisher<uavcan_equipment_gnss_Fix2> gnss_fix2{canard_iface};
Canard::Publisher<uavcan_equipment_gnss_Auxiliary> gnss_auxiliary{canard_iface};
Canard::Publisher<ardupilot_gnss_Heading> gnss_heading{canard_iface};
Canard::Publisher<ardupilot_gnss_Status> gnss_status{canard_iface};
#endif
// incoming messages
Canard::ObjCallback<AP_UAVCAN, ardupilot_indication_Button> safety_button_cb{this, &AP_UAVCAN::handle_button};
Canard::Subscriber<ardupilot_indication_Button> safety_button_listener{safety_button_cb, _driver_index};
Canard::ObjCallback<AP_UAVCAN, ardupilot_equipment_trafficmonitor_TrafficReport> traffic_report_cb{this, &AP_UAVCAN::handle_traffic_report};
Canard::Subscriber<ardupilot_equipment_trafficmonitor_TrafficReport> traffic_report_listener{traffic_report_cb, _driver_index};
Canard::ObjCallback<AP_UAVCAN, uavcan_equipment_actuator_Status> actuator_status_cb{this, &AP_UAVCAN::handle_actuator_status};
Canard::Subscriber<uavcan_equipment_actuator_Status> actuator_status_listener{actuator_status_cb, _driver_index};
Canard::ObjCallback<AP_UAVCAN, uavcan_equipment_esc_Status> esc_status_cb{this, &AP_UAVCAN::handle_ESC_status};
Canard::Subscriber<uavcan_equipment_esc_Status> esc_status_listener{esc_status_cb, _driver_index};
Canard::ObjCallback<AP_UAVCAN, uavcan_protocol_debug_LogMessage> debug_cb{this, &AP_UAVCAN::handle_debug};
Canard::Subscriber<uavcan_protocol_debug_LogMessage> debug_listener{debug_cb, _driver_index};
// param client
Canard::ObjCallback<AP_UAVCAN, uavcan_protocol_param_GetSetResponse> param_get_set_res_cb{this, &AP_UAVCAN::handle_param_get_set_response};
Canard::Client<uavcan_protocol_param_GetSetResponse> param_get_set_client{canard_iface, param_get_set_res_cb};
Canard::ObjCallback<AP_UAVCAN, uavcan_protocol_param_ExecuteOpcodeResponse> param_save_res_cb{this, &AP_UAVCAN::handle_param_save_response};
Canard::Client<uavcan_protocol_param_ExecuteOpcodeResponse> param_save_client{canard_iface, param_save_res_cb};
// reboot client
void handle_restart_node_response(const CanardRxTransfer& transfer, const uavcan_protocol_RestartNodeResponse& msg) {}
Canard::ObjCallback<AP_UAVCAN, uavcan_protocol_RestartNodeResponse> restart_node_res_cb{this, &AP_UAVCAN::handle_restart_node_response};
Canard::Client<uavcan_protocol_RestartNodeResponse> restart_node_client{canard_iface, restart_node_res_cb};
uavcan_protocol_param_ExecuteOpcodeRequest param_save_req;
uavcan_protocol_param_GetSetRequest param_getset_req;
// Node Info Server
Canard::ObjCallback<AP_UAVCAN, uavcan_protocol_GetNodeInfoRequest> node_info_req_cb{this, &AP_UAVCAN::handle_node_info_request};
Canard::Server<uavcan_protocol_GetNodeInfoRequest> node_info_server{canard_iface, node_info_req_cb};
uavcan_protocol_GetNodeInfoResponse node_info_rsp;
// incoming button handling // incoming button handling
static void handle_button(AP_UAVCAN* ap_uavcan, uint8_t node_id, const ButtonCb &cb); void handle_button(const CanardRxTransfer& transfer, const ardupilot_indication_Button& msg);
static void handle_traffic_report(AP_UAVCAN* ap_uavcan, uint8_t node_id, const TrafficReportCb &cb); void handle_traffic_report(const CanardRxTransfer& transfer, const ardupilot_equipment_trafficmonitor_TrafficReport& msg);
static void handle_actuator_status(AP_UAVCAN* ap_uavcan, uint8_t node_id, const ActuatorStatusCb &cb); void handle_actuator_status(const CanardRxTransfer& transfer, const uavcan_equipment_actuator_Status& msg);
static void handle_actuator_status_Volz(AP_UAVCAN* ap_uavcan, uint8_t node_id, const ActuatorStatusVolzCb &cb); void handle_actuator_status_Volz(const CanardRxTransfer& transfer, const uavcan_equipment_actuator_Status& msg);
static void handle_ESC_status(AP_UAVCAN* ap_uavcan, uint8_t node_id, const ESCStatusCb &cb); void handle_ESC_status(const CanardRxTransfer& transfer, const uavcan_equipment_esc_Status& msg);
static bool is_esc_data_index_valid(const uint8_t index); static bool is_esc_data_index_valid(const uint8_t index);
static void handle_debug(AP_UAVCAN* ap_uavcan, uint8_t node_id, const DebugCb &cb); void handle_debug(const CanardRxTransfer& transfer, const uavcan_protocol_debug_LogMessage& msg);
static void handle_param_get_set_response(AP_UAVCAN* ap_uavcan, uint8_t node_id, const ParamGetSetCb &cb); void handle_param_get_set_response(const CanardRxTransfer& transfer, const uavcan_protocol_param_GetSetResponse& rsp);
static void handle_param_save_response(AP_UAVCAN* ap_uavcan, uint8_t node_id, const ParamExecuteOpcodeCb &cb); void handle_param_save_response(const CanardRxTransfer& transfer, const uavcan_protocol_param_ExecuteOpcodeResponse& rsp);
void handle_node_info_request(const CanardRxTransfer& transfer, const uavcan_protocol_GetNodeInfoRequest& req);
}; };
#endif // #if HAL_ENABLE_LIBUAVCAN_DRIVERS #endif // #if HAL_ENABLE_LIBUAVCAN_DRIVERS

View File

@ -1,59 +0,0 @@
/*
* This file is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Siddharth Bharat Purohit
*/
#pragma once
#include <uavcan/uavcan.hpp>
namespace uavcan
{
class SystemClock: public uavcan::ISystemClock, uavcan::Noncopyable
{
public:
SystemClock() = default;
void adjustUtc(uavcan::UtcDuration adjustment) override
{
utc_adjustment_usec = adjustment.toUSec();
}
uavcan::MonotonicTime getMonotonic() const override
{
return uavcan::MonotonicTime::fromUSec(AP_HAL::native_micros64());
}
uavcan::UtcTime getUtc() const override
{
return uavcan::UtcTime::fromUSec(AP_HAL::native_micros64() + utc_adjustment_usec);
}
int64_t getAdjustUsec() const
{
return utc_adjustment_usec;
}
static SystemClock& instance()
{
static SystemClock inst;
return inst;
}
private:
int64_t utc_adjustment_usec;
};
}

View File

@ -24,11 +24,10 @@
#include "AP_UAVCAN.h" #include "AP_UAVCAN.h"
#include <StorageManager/StorageManager.h> #include <StorageManager/StorageManager.h>
#include <AP_Math/AP_Math.h> #include <AP_Math/AP_Math.h>
#include <uavcan/protocol/dynamic_node_id/Allocation.hpp>
#include <GCS_MAVLink/GCS.h> #include <GCS_MAVLink/GCS.h>
#include <AP_Logger/AP_Logger.h> #include <AP_Logger/AP_Logger.h>
#include "AP_UAVCAN_Clock.h"
#include <AP_BoardConfig/AP_BoardConfig.h> #include <AP_BoardConfig/AP_BoardConfig.h>
#include <stdio.h>
extern const AP_HAL::HAL& hal; extern const AP_HAL::HAL& hal;
#define NODEDATA_MAGIC 0xAC01 #define NODEDATA_MAGIC 0xAC01
@ -37,56 +36,15 @@ extern const AP_HAL::HAL& hal;
#define debug_uavcan(level_debug, fmt, args...) do { AP::can().log_text(level_debug, "UAVCAN", fmt, ##args); } while (0) #define debug_uavcan(level_debug, fmt, args...) do { AP::can().log_text(level_debug, "UAVCAN", fmt, ##args); } while (0)
//Callback Object Definitions AP_UAVCAN_DNA_Server::AP_UAVCAN_DNA_Server(AP_UAVCAN &ap_uavcan) :
UC_REGISTRY_BINDER(AllocationCb, uavcan::protocol::dynamic_node_id::Allocation);
UC_REGISTRY_BINDER(NodeStatusCb, uavcan::protocol::NodeStatus);
UC_CLIENT_CALL_REGISTRY_BINDER(GetNodeInfoCb, uavcan::protocol::GetNodeInfo);
static uavcan::ServiceClient<uavcan::protocol::GetNodeInfo, GetNodeInfoCb>* getNodeInfo_client[HAL_MAX_CAN_PROTOCOL_DRIVERS];
static uavcan::Publisher<uavcan::protocol::dynamic_node_id::Allocation>* allocation_pub[HAL_MAX_CAN_PROTOCOL_DRIVERS];
AP_UAVCAN_DNA_Server::AP_UAVCAN_DNA_Server(AP_UAVCAN *ap_uavcan, StorageAccess _storage) :
_ap_uavcan(ap_uavcan), _ap_uavcan(ap_uavcan),
storage(_storage) _canard_iface(ap_uavcan.canard_iface),
storage(StorageManager::StorageCANDNA),
allocation_sub(allocation_cb, _ap_uavcan.get_driver_index()),
node_status_sub(node_status_cb, _ap_uavcan.get_driver_index()),
node_info_client(_canard_iface, node_info_cb)
{} {}
/* Subscribe to all the messages we are going to handle for
Server registry and Node allocation. */
void AP_UAVCAN_DNA_Server::subscribe_msgs(AP_UAVCAN* ap_uavcan)
{
if (ap_uavcan == nullptr) {
return;
}
auto* node = ap_uavcan->get_node();
//Register Allocation Message Handler
uavcan::Subscriber<uavcan::protocol::dynamic_node_id::Allocation, AllocationCb> *AllocationListener;
AllocationListener = new uavcan::Subscriber<uavcan::protocol::dynamic_node_id::Allocation, AllocationCb>(*node);
if (AllocationListener == nullptr) {
AP_BoardConfig::allocation_error("AP_UAVCAN_DNA: AllocationListener");
}
const int alloc_listener_res = AllocationListener->start(AllocationCb(ap_uavcan, &trampoline_handleAllocation));
if (alloc_listener_res < 0) {
AP_HAL::panic("Allocation Subscriber start problem\n\r");
return;
}
//We allow anonymous transfers, as they are specifically for Node Allocation
AllocationListener->allowAnonymousTransfers();
//Register Node Status Listener
uavcan::Subscriber<uavcan::protocol::NodeStatus, NodeStatusCb> *NodeStatusListener;
NodeStatusListener = new uavcan::Subscriber<uavcan::protocol::NodeStatus, NodeStatusCb>(*node);
if (NodeStatusListener == nullptr) {
AP_BoardConfig::allocation_error("AP_UAVCAN_DNA: NodeStatusListener");
}
const int nodestatus_listener_res = NodeStatusListener->start(NodeStatusCb(ap_uavcan, &trampoline_handleNodeStatus));
if (nodestatus_listener_res < 0) {
AP_HAL::panic("NodeStatus Subscriber start problem\n\r");
return;
}
}
/* Method to generate 6byte hash from the Unique ID. /* Method to generate 6byte hash from the Unique ID.
We return it packed inside the referenced NodeData structure */ We return it packed inside the referenced NodeData structure */
void AP_UAVCAN_DNA_Server::getHash(NodeData &node_data, const uint8_t unique_id[], uint8_t size) const void AP_UAVCAN_DNA_Server::getHash(NodeData &node_data, const uint8_t unique_id[], uint8_t size) const
@ -255,43 +213,10 @@ bool AP_UAVCAN_DNA_Server::isValidNodeDataAvailable(uint8_t node_id)
Also resets the Server Record in case there is a mismatch Also resets the Server Record in case there is a mismatch
between specified node id and unique id against the existing between specified node id and unique id against the existing
Server Record. */ Server Record. */
bool AP_UAVCAN_DNA_Server::init() bool AP_UAVCAN_DNA_Server::init(uint8_t own_unique_id[], uint8_t own_unique_id_len, uint8_t node_id)
{ {
//Read the details from ap_uavcan //Read the details from AP_UAVCAN
uavcan::Node<0>* _node = _ap_uavcan->get_node();
uint8_t node_id = _node->getNodeID().get();
uint8_t own_unique_id[16] = {0};
uint8_t own_unique_id_len = 16;
driver_index = _ap_uavcan->get_driver_index();
//copy unique id from node to uint8_t array
uavcan::copy(_node->getHardwareVersion().unique_id.begin(),
_node->getHardwareVersion().unique_id.end(),
own_unique_id);
server_state = HEALTHY; server_state = HEALTHY;
//Setup publisher for this driver index
allocation_pub[driver_index] = new uavcan::Publisher<uavcan::protocol::dynamic_node_id::Allocation>(*_node, true);
if (allocation_pub[driver_index] == nullptr) {
AP_BoardConfig::allocation_error("AP_UAVCAN_DNA: allocation_pub[%d]", driver_index);
}
int res = allocation_pub[driver_index]->init(uavcan::TransferPriority::Default);
if (res < 0) {
return false;
}
allocation_pub[driver_index]->setTxTimeout(uavcan::MonotonicDuration::fromMSec(uavcan::protocol::dynamic_node_id::Allocation::FOLLOWUP_TIMEOUT_MS));
//Setup GetNodeInfo Client
getNodeInfo_client[driver_index] = new uavcan::ServiceClient<uavcan::protocol::GetNodeInfo, GetNodeInfoCb>(*_node, GetNodeInfoCb(_ap_uavcan, &trampoline_handleNodeInfo));
if (getNodeInfo_client[driver_index] == nullptr) {
AP_BoardConfig::allocation_error("AP_UAVCAN_DNA: getNodeInfo_client[%d]", driver_index);
}
/* Go through our records and look for valid NodeData, to initialise /* Go through our records and look for valid NodeData, to initialise
occupation mask */ occupation mask */
for (uint8_t i = 0; i <= MAX_NODE_ID; i++) { for (uint8_t i = 0; i <= MAX_NODE_ID; i++) {
@ -310,7 +235,7 @@ bool AP_UAVCAN_DNA_Server::init()
//Its not there a reset should write it in the Storage //Its not there a reset should write it in the Storage
reset(); reset();
} }
if (_ap_uavcan->check_and_reset_option(AP_UAVCAN::Options::DNA_CLEAR_DATABASE)) { if (_ap_uavcan.check_and_reset_option(AP_UAVCAN::Options::DNA_CLEAR_DATABASE)) {
GCS_SEND_TEXT(MAV_SEVERITY_INFO, "UC DNA database reset"); GCS_SEND_TEXT(MAV_SEVERITY_INFO, "UC DNA database reset");
reset(); reset();
} }
@ -350,7 +275,7 @@ bool AP_UAVCAN_DNA_Server::init()
addToSeenNodeMask(node_id); addToSeenNodeMask(node_id);
setVerificationMask(node_id); setVerificationMask(node_id);
node_healthy_mask.set(node_id); node_healthy_mask.set(node_id);
self_node_id[driver_index] = node_id; self_node_id = node_id;
return true; return true;
} }
@ -422,7 +347,7 @@ seen list, So that we can raise issue if there are duplicates
on the bus. */ on the bus. */
void AP_UAVCAN_DNA_Server::verify_nodes() void AP_UAVCAN_DNA_Server::verify_nodes()
{ {
uint32_t now = uavcan::SystemClock::instance().getMonotonic().toMSec(); uint32_t now = AP_HAL::millis();
if ((now - last_verification_request) < 5000) { if ((now - last_verification_request) < 5000) {
return; return;
} }
@ -435,7 +360,7 @@ void AP_UAVCAN_DNA_Server::verify_nodes()
//Check if we got acknowledgement from previous request //Check if we got acknowledgement from previous request
//except for requests using our own node_id //except for requests using our own node_id
if (curr_verifying_node == self_node_id[driver_index]) { if (curr_verifying_node == self_node_id) {
nodeInfo_resp_rcvd = true; nodeInfo_resp_rcvd = true;
} }
@ -456,13 +381,16 @@ void AP_UAVCAN_DNA_Server::verify_nodes()
//Find the next registered Node ID to be verified. //Find the next registered Node ID to be verified.
for (uint8_t i = 0; i <= MAX_NODE_ID; i++) { for (uint8_t i = 0; i <= MAX_NODE_ID; i++) {
curr_verifying_node = (curr_verifying_node + 1) % (MAX_NODE_ID + 1); curr_verifying_node = (curr_verifying_node + 1) % (MAX_NODE_ID + 1);
if ((curr_verifying_node == self_node_id) || (curr_verifying_node == 0)) {
continue;
}
if (isNodeSeen(curr_verifying_node)) { if (isNodeSeen(curr_verifying_node)) {
break; break;
} }
} }
if (getNodeInfo_client[driver_index] != nullptr && isNodeIDOccupied(curr_verifying_node)) { if (isNodeIDOccupied(curr_verifying_node)) {
uavcan::protocol::GetNodeInfo::Request request; uavcan_protocol_GetNodeInfoRequest request;
getNodeInfo_client[driver_index]->call(curr_verifying_node, request); node_info_client.request(curr_verifying_node, request);
nodeInfo_resp_rcvd = false; nodeInfo_resp_rcvd = false;
} }
} }
@ -470,63 +398,50 @@ void AP_UAVCAN_DNA_Server::verify_nodes()
/* Handles Node Status Message, adds to the Seen Node list /* Handles Node Status Message, adds to the Seen Node list
Also starts the Service call for Node Info to complete the Also starts the Service call for Node Info to complete the
Verification process. */ Verification process. */
void AP_UAVCAN_DNA_Server::handleNodeStatus(uint8_t node_id, const NodeStatusCb &cb) void AP_UAVCAN_DNA_Server::handleNodeStatus(const CanardRxTransfer& transfer, const uavcan_protocol_NodeStatus& msg)
{ {
if (node_id > MAX_NODE_ID) { if (transfer.source_node_id > MAX_NODE_ID || transfer.source_node_id == 0) {
return; return;
} }
if ((cb.msg->health != uavcan::protocol::NodeStatus::HEALTH_OK || if ((msg.health != UAVCAN_PROTOCOL_NODESTATUS_HEALTH_OK ||
cb.msg->mode != uavcan::protocol::NodeStatus::MODE_OPERATIONAL) && msg.mode != UAVCAN_PROTOCOL_NODESTATUS_MODE_OPERATIONAL) &&
!_ap_uavcan->option_is_set(AP_UAVCAN::Options::DNA_IGNORE_UNHEALTHY_NODE)) { !_ap_uavcan.option_is_set(AP_UAVCAN::Options::DNA_IGNORE_UNHEALTHY_NODE)) {
//if node is not healthy or operational, clear resp health mask, and set fault_node_id //if node is not healthy or operational, clear resp health mask, and set fault_node_id
fault_node_id = node_id; fault_node_id = transfer.source_node_id;
server_state = NODE_STATUS_UNHEALTHY; server_state = NODE_STATUS_UNHEALTHY;
node_healthy_mask.clear(node_id); node_healthy_mask.clear(transfer.source_node_id);
} else { } else {
node_healthy_mask.set(node_id); node_healthy_mask.set(transfer.source_node_id);
if (node_healthy_mask == verified_mask) { if (node_healthy_mask == verified_mask) {
server_state = HEALTHY; server_state = HEALTHY;
} }
} }
if (!isNodeIDVerified(node_id)) { if (!isNodeIDVerified(transfer.source_node_id)) {
//immediately begin verification of the node_id //immediately begin verification of the node_id
if (getNodeInfo_client[driver_index] != nullptr) { uavcan_protocol_GetNodeInfoRequest request;
uavcan::protocol::GetNodeInfo::Request request; node_info_client.request(transfer.source_node_id, request);
getNodeInfo_client[driver_index]->call(node_id, request);
}
} }
//Add node to seen list if not seen before //Add node to seen list if not seen before
addToSeenNodeMask(node_id); addToSeenNodeMask(transfer.source_node_id);
} }
//Trampoline call for handleNodeStatus member method
void AP_UAVCAN_DNA_Server::trampoline_handleNodeStatus(AP_UAVCAN* ap_uavcan, uint8_t node_id, const NodeStatusCb &cb)
{
if (ap_uavcan == nullptr) {
return;
}
ap_uavcan->_dna_server->handleNodeStatus(node_id, cb);
}
/* Node Info message handler /* Node Info message handler
Handle responses from GetNodeInfo Request. We verify the node info Handle responses from GetNodeInfo Request. We verify the node info
against our records. Marks Verification mask if already recorded, against our records. Marks Verification mask if already recorded,
Or register if the node id is available and not recorded for the Or register if the node id is available and not recorded for the
received Unique ID */ received Unique ID */
void AP_UAVCAN_DNA_Server::handleNodeInfo(uint8_t node_id, uint8_t unique_id[], char name[], uint8_t major, uint8_t minor, uint32_t vcs_commit) void AP_UAVCAN_DNA_Server::handleNodeInfo(const CanardRxTransfer& transfer, const uavcan_protocol_GetNodeInfoResponse& rsp)
{ {
if (node_id > MAX_NODE_ID) { if (transfer.source_node_id > MAX_NODE_ID) {
return; return;
} }
/* /*
if we haven't logged this node then log it now if we haven't logged this node then log it now
*/ */
if (!logged.get(node_id) && AP::logger().logging_started()) { if (!logged.get(transfer.source_node_id) && AP::logger().logging_started()) {
logged.set(node_id); logged.set(transfer.source_node_id);
uint64_t uid[2]; uint64_t uid[2];
memcpy(uid, unique_id, sizeof(uid)); memcpy(uid, rsp.hardware_version.unique_id, sizeof(rsp.hardware_version.unique_id));
// @LoggerMessage: CAND // @LoggerMessage: CAND
// @Description: Info from GetNodeInfo request // @Description: Info from GetNodeInfo request
// @Field: TimeUS: Time since system startup // @Field: TimeUS: Time since system startup
@ -540,105 +455,82 @@ void AP_UAVCAN_DNA_Server::handleNodeInfo(uint8_t node_id, uint8_t unique_id[],
AP::logger().Write("CAND", "TimeUS,NodeId,UID1,UID2,Name,Major,Minor,Version", AP::logger().Write("CAND", "TimeUS,NodeId,UID1,UID2,Name,Major,Minor,Version",
"s#------", "F-------", "QBQQZBBI", "s#------", "F-------", "QBQQZBBI",
AP_HAL::micros64(), AP_HAL::micros64(),
node_id, transfer.source_node_id,
uid[0], uid[1], uid[0], uid[1],
name, rsp.name.data,
major, rsp.software_version.major,
minor, rsp.software_version.minor,
vcs_commit); rsp.software_version.vcs_commit);
} }
if (isNodeIDOccupied(node_id)) { if (isNodeIDOccupied(transfer.source_node_id)) {
//if node_id already registered, just verify if Unique ID matches as well //if node_id already registered, just verify if Unique ID matches as well
if (node_id == getNodeIDForUniqueID(unique_id, 16)) { if (transfer.source_node_id == getNodeIDForUniqueID(rsp.hardware_version.unique_id, 16)) {
if (node_id == curr_verifying_node) { if (transfer.source_node_id == curr_verifying_node) {
nodeInfo_resp_rcvd = true; nodeInfo_resp_rcvd = true;
} }
setVerificationMask(node_id); setVerificationMask(transfer.source_node_id);
} else if (!_ap_uavcan->option_is_set(AP_UAVCAN::Options::DNA_IGNORE_DUPLICATE_NODE)) { } else if (!_ap_uavcan.option_is_set(AP_UAVCAN::Options::DNA_IGNORE_DUPLICATE_NODE)) {
/* This is a device with node_id already registered /* This is a device with node_id already registered
for another device */ for another device */
server_state = DUPLICATE_NODES; server_state = DUPLICATE_NODES;
fault_node_id = node_id; fault_node_id = transfer.source_node_id;
memcpy(fault_node_name, name, sizeof(fault_node_name)); memcpy(fault_node_name, rsp.name.data, sizeof(fault_node_name));
} }
} else { } else {
/* Node Id was not allocated by us, or during this boot, let's register this in our records /* Node Id was not allocated by us, or during this boot, let's register this in our records
Check if we allocated this Node before */ Check if we allocated this Node before */
uint8_t prev_node_id = getNodeIDForUniqueID(unique_id, 16); uint8_t prev_node_id = getNodeIDForUniqueID(rsp.hardware_version.unique_id, 16);
if (prev_node_id != 255) { if (prev_node_id != 255) {
//yes we did, remove this registration //yes we did, remove this registration
freeNodeID(prev_node_id); freeNodeID(prev_node_id);
} }
//add a new server record //add a new server record
addNodeIDForUniqueID(node_id, unique_id, 16); addNodeIDForUniqueID(transfer.source_node_id, rsp.hardware_version.unique_id, 16);
//Verify as well //Verify as well
setVerificationMask(node_id); setVerificationMask(transfer.source_node_id);
if (node_id == curr_verifying_node) { if (transfer.source_node_id == curr_verifying_node) {
nodeInfo_resp_rcvd = true; nodeInfo_resp_rcvd = true;
} }
} }
} }
//Trampoline call for handleNodeInfo member call
void AP_UAVCAN_DNA_Server::trampoline_handleNodeInfo(AP_UAVCAN* ap_uavcan, uint8_t node_id, const GetNodeInfoCb& resp)
{
uint8_t unique_id[16] = {0};
char name[50] = {0};
//copy the unique id from message to uint8_t array
auto &r = resp.rsp->getResponse();
uavcan::copy(r.hardware_version.unique_id.begin(),
r.hardware_version.unique_id.end(),
unique_id);
strncpy_noterm(name, r.name.c_str(), sizeof(name)-1);
ap_uavcan->_dna_server->handleNodeInfo(node_id, unique_id, name,
r.software_version.major,
r.software_version.minor,
r.software_version.vcs_commit);
}
/* Handle the allocation message from the devices supporting /* Handle the allocation message from the devices supporting
dynamic node allocation. */ dynamic node allocation. */
void AP_UAVCAN_DNA_Server::handleAllocation(uint8_t node_id, const AllocationCb &cb) void AP_UAVCAN_DNA_Server::handleAllocation(const CanardRxTransfer& transfer, const uavcan_protocol_dynamic_node_id_Allocation& msg)
{ {
if (allocation_pub[driver_index] == nullptr) { if (transfer.source_node_id != 0) {
//init has not been called for this driver.
return;
}
if (!cb.msg->isAnonymousTransfer()) {
//Ignore Allocation messages that are not DNA requests //Ignore Allocation messages that are not DNA requests
return; return;
} }
uint32_t now = uavcan::SystemClock::instance().getMonotonic().toMSec(); uint32_t now = AP_HAL::millis();
if (rcvd_unique_id_offset == 0 || if (rcvd_unique_id_offset == 0 ||
(now - last_alloc_msg_ms) > 500) { (now - last_alloc_msg_ms) > 500) {
if (cb.msg->first_part_of_unique_id) { if (msg.first_part_of_unique_id) {
rcvd_unique_id_offset = 0; rcvd_unique_id_offset = 0;
memset(rcvd_unique_id, 0, sizeof(rcvd_unique_id)); memset(rcvd_unique_id, 0, sizeof(rcvd_unique_id));
} else { } else {
//we are only accepting first part //we are only accepting first part
return; return;
} }
} else if (cb.msg->first_part_of_unique_id) { } else if (msg.first_part_of_unique_id) {
// we are only accepting follow up messages // we are only accepting follow up messages
return; return;
} }
if (rcvd_unique_id_offset) { if (rcvd_unique_id_offset) {
debug_uavcan(AP_CANManager::LOG_DEBUG, "TIME: %ld -- Accepting Followup part! %u\n", debug_uavcan(AP_CANManager::LOG_DEBUG, "TIME: %ld -- Accepting Followup part! %u\n",
long(uavcan::SystemClock::instance().getMonotonic().toUSec()/1000), (long int)AP_HAL::millis(),
unsigned((now - last_alloc_msg_ms))); unsigned((now - last_alloc_msg_ms)));
} else { } else {
debug_uavcan(AP_CANManager::LOG_DEBUG, "TIME: %ld -- Accepting First part! %u\n", debug_uavcan(AP_CANManager::LOG_DEBUG, "TIME: %ld -- Accepting First part! %u\n",
long(uavcan::SystemClock::instance().getMonotonic().toUSec()/1000), (long int)AP_HAL::millis(),
unsigned((now - last_alloc_msg_ms))); unsigned((now - last_alloc_msg_ms)));
} }
last_alloc_msg_ms = now; last_alloc_msg_ms = now;
if ((rcvd_unique_id_offset + cb.msg->unique_id.size()) > 16) { if ((rcvd_unique_id_offset + msg.unique_id.len) > 16) {
//This request is malformed, Reset! //This request is malformed, Reset!
rcvd_unique_id_offset = 0; rcvd_unique_id_offset = 0;
memset(rcvd_unique_id, 0, sizeof(rcvd_unique_id)); memset(rcvd_unique_id, 0, sizeof(rcvd_unique_id));
@ -646,50 +538,40 @@ void AP_UAVCAN_DNA_Server::handleAllocation(uint8_t node_id, const AllocationCb
} }
//copy over the unique_id //copy over the unique_id
for (uint8_t i=rcvd_unique_id_offset; i<(rcvd_unique_id_offset + cb.msg->unique_id.size()); i++) { for (uint8_t i=rcvd_unique_id_offset; i<(rcvd_unique_id_offset + msg.unique_id.len); i++) {
rcvd_unique_id[i] = cb.msg->unique_id[i - rcvd_unique_id_offset]; rcvd_unique_id[i] = msg.unique_id.data[i - rcvd_unique_id_offset];
} }
rcvd_unique_id_offset += cb.msg->unique_id.size(); rcvd_unique_id_offset += msg.unique_id.len;
//send follow up message //send follow up message
uavcan::protocol::dynamic_node_id::Allocation msg; uavcan_protocol_dynamic_node_id_Allocation rsp;
/* Respond with the message containing the received unique ID so far /* Respond with the message containing the received unique ID so far
or with node id if we successfully allocated one. */ or with node id if we successfully allocated one. */
for (uint8_t i = 0; i < rcvd_unique_id_offset; i++) { memcpy(rsp.unique_id.data, rcvd_unique_id, rcvd_unique_id_offset);
msg.unique_id.push_back(rcvd_unique_id[i]); rsp.unique_id.len = rcvd_unique_id_offset;
}
if (rcvd_unique_id_offset == 16) { if (rcvd_unique_id_offset == 16) {
//We have received the full Unique ID, time to do allocation //We have received the full Unique ID, time to do allocation
uint8_t resp_node_id = getNodeIDForUniqueID((const uint8_t*)rcvd_unique_id, 16); uint8_t resp_node_id = getNodeIDForUniqueID((const uint8_t*)rcvd_unique_id, 16);
if (resp_node_id == 255) { if (resp_node_id == 255) {
resp_node_id = findFreeNodeID(cb.msg->node_id); resp_node_id = findFreeNodeID(msg.node_id);
if (resp_node_id != 255) { if (resp_node_id != 255) {
if (addNodeIDForUniqueID(resp_node_id, (const uint8_t*)rcvd_unique_id, 16)) { if (addNodeIDForUniqueID(resp_node_id, (const uint8_t*)rcvd_unique_id, 16)) {
msg.node_id = resp_node_id; rsp.node_id = resp_node_id;
} }
} else { } else {
GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "UC Node Alloc Failed!"); GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "UC Node Alloc Failed!");
} }
} else { } else {
msg.node_id = resp_node_id; rsp.node_id = resp_node_id;
} }
//reset states as well //reset states as well
rcvd_unique_id_offset = 0; rcvd_unique_id_offset = 0;
memset(rcvd_unique_id, 0, sizeof(rcvd_unique_id)); memset(rcvd_unique_id, 0, sizeof(rcvd_unique_id));
} }
allocation_pub[driver_index]->broadcast(msg); allocation_pub.broadcast(rsp, false); // never publish allocation message with CAN FD
}
//Trampoline Call for handleAllocation member call
void AP_UAVCAN_DNA_Server::trampoline_handleAllocation(AP_UAVCAN* ap_uavcan, uint8_t node_id, const AllocationCb &cb)
{
if (ap_uavcan == nullptr) {
return;
}
ap_uavcan->_dna_server->handleAllocation(node_id, cb);
} }
//report the server state, along with failure message if any //report the server state, along with failure message if any
@ -703,7 +585,7 @@ bool AP_UAVCAN_DNA_Server::prearm_check(char* fail_msg, uint8_t fail_msg_len) co
return false; return false;
} }
case DUPLICATE_NODES: { case DUPLICATE_NODES: {
if (_ap_uavcan->option_is_set(AP_UAVCAN::Options::DNA_IGNORE_DUPLICATE_NODE)) { if (_ap_uavcan.option_is_set(AP_UAVCAN::Options::DNA_IGNORE_DUPLICATE_NODE)) {
// ignore error // ignore error
return true; return true;
} }
@ -715,7 +597,7 @@ bool AP_UAVCAN_DNA_Server::prearm_check(char* fail_msg, uint8_t fail_msg_len) co
return false; return false;
} }
case NODE_STATUS_UNHEALTHY: { case NODE_STATUS_UNHEALTHY: {
if (_ap_uavcan->option_is_set(AP_UAVCAN::Options::DNA_IGNORE_UNHEALTHY_NODE)) { if (_ap_uavcan.option_is_set(AP_UAVCAN::Options::DNA_IGNORE_UNHEALTHY_NODE)) {
// ignore error // ignore error
return true; return true;
} }

View File

@ -3,18 +3,17 @@
#include <AP_HAL/Semaphores.h> #include <AP_HAL/Semaphores.h>
#if HAL_ENABLE_LIBUAVCAN_DRIVERS #if HAL_ENABLE_LIBUAVCAN_DRIVERS
#include <uavcan/uavcan.hpp>
#include <AP_Common/Bitmask.h> #include <AP_Common/Bitmask.h>
#include <StorageManager/StorageManager.h> #include <StorageManager/StorageManager.h>
#include <AP_CANManager/AP_CANManager.h> #include <AP_CANManager/AP_CANManager.h>
#include <canard/publisher.h>
#include <canard/subscriber.h>
#include <canard/service_client.h>
#include "AP_Canard_iface.h"
#include <dronecan_msgs.h>
//Forward declaring classes
class AllocationCb;
class NodeStatusCb;
class NodeInfoCb;
class GetNodeInfoCb;
class AP_UAVCAN; class AP_UAVCAN;
//Forward declaring classes
class AP_UAVCAN_DNA_Server class AP_UAVCAN_DNA_Server
{ {
StorageAccess storage; StorageAccess storage;
@ -34,7 +33,7 @@ class AP_UAVCAN_DNA_Server
uint32_t last_verification_request; uint32_t last_verification_request;
uint8_t curr_verifying_node; uint8_t curr_verifying_node;
uint8_t self_node_id[HAL_MAX_CAN_PROTOCOL_DRIVERS]; uint8_t self_node_id;
bool nodeInfo_resp_rcvd; bool nodeInfo_resp_rcvd;
Bitmask<128> occupation_mask; Bitmask<128> occupation_mask;
@ -89,24 +88,30 @@ class AP_UAVCAN_DNA_Server
//Look in the storage and check if there's a valid Server Record there //Look in the storage and check if there's a valid Server Record there
bool isValidNodeDataAvailable(uint8_t node_id); bool isValidNodeDataAvailable(uint8_t node_id);
static void trampoline_handleNodeInfo(AP_UAVCAN* ap_uavcan, uint8_t node_id, const GetNodeInfoCb& resp);
static void trampoline_handleAllocation(AP_UAVCAN* ap_uavcan, uint8_t node_id, const AllocationCb &cb);
static void trampoline_handleNodeStatus(AP_UAVCAN* ap_uavcan, uint8_t node_id, const NodeStatusCb &cb);
HAL_Semaphore storage_sem; HAL_Semaphore storage_sem;
AP_UAVCAN *_ap_uavcan; AP_UAVCAN &_ap_uavcan;
uint8_t driver_index; CanardInterface &_canard_iface;
Canard::Publisher<uavcan_protocol_dynamic_node_id_Allocation> allocation_pub{_canard_iface};
Canard::ObjCallback<AP_UAVCAN_DNA_Server, uavcan_protocol_dynamic_node_id_Allocation> allocation_cb{this, &AP_UAVCAN_DNA_Server::handleAllocation};
Canard::Subscriber<uavcan_protocol_dynamic_node_id_Allocation> allocation_sub;
Canard::ObjCallback<AP_UAVCAN_DNA_Server, uavcan_protocol_NodeStatus> node_status_cb{this, &AP_UAVCAN_DNA_Server::handleNodeStatus};
Canard::Subscriber<uavcan_protocol_NodeStatus> node_status_sub;
Canard::ObjCallback<AP_UAVCAN_DNA_Server, uavcan_protocol_GetNodeInfoResponse> node_info_cb{this, &AP_UAVCAN_DNA_Server::handleNodeInfo};
Canard::Client<uavcan_protocol_GetNodeInfoResponse> node_info_client;
public: public:
AP_UAVCAN_DNA_Server(AP_UAVCAN *ap_uavcan, StorageAccess _storage); AP_UAVCAN_DNA_Server(AP_UAVCAN &ap_uavcan);
// Do not allow copies // Do not allow copies
CLASS_NO_COPY(AP_UAVCAN_DNA_Server); CLASS_NO_COPY(AP_UAVCAN_DNA_Server);
//Initialises publisher and Server Record for specified uavcan driver //Initialises publisher and Server Record for specified uavcan driver
bool init(); bool init(uint8_t own_unique_id[], uint8_t own_unique_id_len, uint8_t node_id);
//Reset the Server Record //Reset the Server Record
void reset(); void reset();
@ -125,9 +130,9 @@ public:
bool prearm_check(char* fail_msg, uint8_t fail_msg_len) const; bool prearm_check(char* fail_msg, uint8_t fail_msg_len) const;
//Callbacks //Callbacks
void handleAllocation(uint8_t node_id, const AllocationCb &cb); void handleAllocation(const CanardRxTransfer& transfer, const uavcan_protocol_dynamic_node_id_Allocation& msg);
void handleNodeStatus(uint8_t node_id, const NodeStatusCb &cb); void handleNodeStatus(const CanardRxTransfer& transfer, const uavcan_protocol_NodeStatus& msg);
void handleNodeInfo(uint8_t node_id, uint8_t unique_id[], char name[], uint8_t major, uint8_t minor, uint32_t vcs_commit); void handleNodeInfo(const CanardRxTransfer& transfer, const uavcan_protocol_GetNodeInfoResponse& rsp);
//Run through the list of seen node ids for verification //Run through the list of seen node ids for verification
void verify_nodes(); void verify_nodes();

View File

@ -1,238 +0,0 @@
/*
* This file is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Siddharth Bharat Purohit
*/
#include "AP_UAVCAN_IfaceMgr.h"
#if HAL_ENABLE_LIBUAVCAN_DRIVERS
#include "AP_UAVCAN_Clock.h"
#include <AP_HAL/AP_HAL.h>
#include <AP_CANManager/AP_CANManager.h>
using namespace uavcan;
extern const AP_HAL::HAL& hal;
#define LOG_TAG "UAVCANIface"
/*****************************************************
* *
* CAN Iface *
* *
* ***************************************************/
/**
* Non-blocking transmission.
*
* If the frame wasn't transmitted upon TX deadline, the driver should discard it.
*
* Note that it is LIKELY that the library will want to send the frames that were passed into the select()
* method as the next ones to transmit, but it is NOT guaranteed. The library can replace those with new
* frames between the calls.
*
* @return 1 = one frame transmitted, 0 = TX buffer full, negative for error.
*/
int16_t CanIface::send(const CanFrame& frame, MonotonicTime tx_deadline, CanIOFlags flags)
{
if (can_iface_ == UAVCAN_NULLPTR) {
return -1;
}
return can_iface_->send(AP_HAL::CANFrame(frame.id, frame.data, AP_HAL::CANFrame::dlcToDataLength(frame.dlc), frame.isCanFDFrame()), tx_deadline.toUSec(), flags);
}
/**
* Non-blocking reception.
*
* Timestamps should be provided by the CAN driver, ideally by the hardware CAN controller.
*
* Monotonic timestamp is required and can be not precise since it is needed only for
* protocol timing validation (transfer timeouts and inter-transfer intervals).
*
* UTC timestamp is optional, if available it will be used for precise time synchronization;
* must be set to zero if not available.
*
* Refer to @ref ISystemClock to learn more about timestamps.
*
* @param [out] out_ts_monotonic Monotonic timestamp, mandatory.
* @param [out] out_ts_utc UTC timestamp, optional, zero if unknown.
* @return 1 = one frame received, 0 = RX buffer empty, negative for error.
*/
int16_t CanIface::receive(CanFrame& out_frame, MonotonicTime& out_ts_monotonic, UtcTime& out_ts_utc,
CanIOFlags& out_flags)
{
if (can_iface_ == UAVCAN_NULLPTR) {
return -1;
}
AP_HAL::CANFrame frame;
uint64_t rx_timestamp;
uint16_t flags;
out_ts_monotonic = SystemClock::instance().getMonotonic();
int16_t ret = can_iface_->receive(frame, rx_timestamp, flags);
if (ret < 0) {
return ret;
}
out_frame = CanFrame(frame.id, (const uint8_t*)frame.data, AP_HAL::CANFrame::dlcToDataLength(frame.dlc), frame.canfd);
out_flags = flags;
if (rx_timestamp != 0) {
out_ts_utc = uavcan::UtcTime::fromUSec(SystemClock::instance().getAdjustUsec() + rx_timestamp);
} else {
out_ts_utc = uavcan::UtcTime::fromUSec(0);
}
return ret;
}
/**
* Number of available hardware filters.
*/
uint16_t CanIface::getNumFilters() const
{
if (can_iface_ == UAVCAN_NULLPTR) {
return 0;
}
return can_iface_->getNumFilters();
}
/**
* Continuously incrementing counter of hardware errors.
* Arbitration lost should not be treated as a hardware error.
*/
uint64_t CanIface::getErrorCount() const
{
if (can_iface_ == UAVCAN_NULLPTR) {
return 0;
}
return can_iface_->getErrorCount();
}
/*****************************************************
* *
* CAN Driver *
* *
* ***************************************************/
bool CanIfaceMgr::add_interface(AP_HAL::CANIface *can_iface)
{
if (num_ifaces > HAL_NUM_CAN_IFACES) {
AP::can().log_text(AP_CANManager::LOG_ERROR, LOG_TAG, "UAVCANIfaceMgr: Num Ifaces Exceeded\n");
return false;
}
if (can_iface == nullptr) {
AP::can().log_text(AP_CANManager::LOG_ERROR, LOG_TAG, "UAVCANIfaceMgr: Iface Null\n");
return false;
}
if (ifaces[num_ifaces] != nullptr) {
AP::can().log_text(AP_CANManager::LOG_ERROR, LOG_TAG, "UAVCANIfaceMgr: Iface already added\n");
return false;
}
ifaces[num_ifaces] = new CanIface(can_iface);
if (ifaces[num_ifaces] == nullptr) {
AP::can().log_text(AP_CANManager::LOG_ERROR, LOG_TAG, "UAVCANIfaceMgr: Can't alloc uavcan::iface\n");
return false;
}
if (!ifaces[num_ifaces]->can_iface_->set_event_handle(&_event_handle)) {
AP::can().log_text(AP_CANManager::LOG_ERROR, LOG_TAG, "UAVCANIfaceMgr: Setting event handle failed\n");
return false;
}
AP::can().log_text(AP_CANManager::LOG_INFO, LOG_TAG, "UAVCANIfaceMgr: Successfully added interface %d\n", int(num_ifaces));
num_ifaces++;
return true;
}
/**
* Returns an interface by index, or null pointer if the index is out of range.
*/
ICanIface* CanIfaceMgr::getIface(uint8_t iface_index)
{
if (iface_index >= num_ifaces) {
return UAVCAN_NULLPTR;
}
return ifaces[iface_index];
}
/**
* Total number of available CAN interfaces.
* This value shall not change after initialization.
*/
uint8_t CanIfaceMgr::getNumIfaces() const
{
return num_ifaces;
}
CanSelectMasks CanIfaceMgr::makeSelectMasks(const CanSelectMasks in_mask, const CanFrame* (& pending_tx)[MaxCanIfaces]) const
{
CanSelectMasks msk;
for (uint8_t i = 0; i < num_ifaces; i++) {
bool read = in_mask.read & (1 << i);
bool write = in_mask.write & (1 << i);
CanIface* iface = ifaces[i];
if (iface == nullptr) {
continue;
}
if (pending_tx[i] == UAVCAN_NULLPTR) {
if (iface->can_iface_->select(read, write, nullptr, 0)) {
msk.read |= (read ? 1 : 0) << i;
msk.write |= (write ? 1 : 0) << i;
}
} else {
AP_HAL::CANFrame frame {pending_tx[i]->id, pending_tx[i]->data, AP_HAL::CANFrame::dlcToDataLength(pending_tx[i]->dlc)};
if (iface->can_iface_->select(read, write, &frame, 0)) {
msk.read |= (read ? 1 : 0) << i;
msk.write |= (write ? 1 : 0) << i;
}
}
}
return msk;
}
/**
* Block until the deadline, or one of the specified interfaces becomes available for read or write.
*
* Iface masks will be modified by the driver to indicate which exactly interfaces are available for IO.
*
* Bit position in the masks defines interface index.
*
* Note that it is allowed to return from this method even if no requested events actually happened, or if
* there are events that were not requested by the library.
*
* The pending TX argument contains an array of pointers to CAN frames that the library wants to transmit
* next, per interface. This is intended to allow the driver to properly prioritize transmissions; many
* drivers will not need to use it. If a write flag for the given interface is set to one in the select mask
* structure, then the corresponding pointer is guaranteed to be valid (not UAVCAN_NULLPTR).
*
* @param [in,out] inout_masks Masks indicating which interfaces are needed/available for IO.
* @param [in] pending_tx Array of frames, per interface, that are likely to be transmitted next.
* @param [in] blocking_deadline Zero means non-blocking operation.
* @return Positive number of ready interfaces or negative error code.
*/
int16_t CanIfaceMgr::select(CanSelectMasks& inout_masks,
const CanFrame* (& pending_tx)[MaxCanIfaces],
const MonotonicTime blocking_deadline)
{
const CanSelectMasks in_masks = inout_masks;
const uint64_t time = SystemClock::instance().getMonotonic().toUSec();
inout_masks = makeSelectMasks(in_masks, pending_tx); // Check if we already have some of the requested events
if ((inout_masks.read & in_masks.read) != 0 ||
(inout_masks.write & in_masks.write) != 0) {
return 1;
}
if (time < blocking_deadline.toUSec()) {
_event_handle.wait(blocking_deadline.toUSec() - time); // Block until timeout expires or any iface updates
}
inout_masks = makeSelectMasks(in_masks, pending_tx); // Return what we got even if none of the requested events are set
return 1; // Return value doesn't matter as long as it is non-negative
}
#endif //HAL_ENABLE_LIBUAVCAN_DRIVERSs

View File

@ -1,75 +0,0 @@
/*
* This file is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Siddharth Bharat Purohit
*/
#pragma once
#include <AP_HAL/AP_HAL.h>
#if HAL_ENABLE_LIBUAVCAN_DRIVERS
#include <uavcan/uavcan.hpp>
namespace uavcan
{
class CanDriver;
class CanIface : public ICanIface, Noncopyable
{
friend class CanIfaceMgr;
AP_HAL::CANIface* can_iface_;
public:
CanIface(AP_HAL::CANIface *can_iface) : can_iface_(can_iface) {}
virtual int16_t send(const CanFrame& frame, MonotonicTime tx_deadline,
CanIOFlags flags) override;
virtual int16_t receive(CanFrame& out_frame, MonotonicTime& out_ts_monotonic,
UtcTime& out_ts_utc, CanIOFlags& out_flags) override;
virtual int16_t configureFilters(const CanFilterConfig* filter_configs,
uint16_t num_configs) override {
return 0;
}
uint16_t getNumFilters() const override;
uint64_t getErrorCount() const override;
};
/**
* Generic CAN driver.
*/
class CanIfaceMgr : public ICanDriver, Noncopyable
{
CanIface* ifaces[HAL_NUM_CAN_IFACES];
uint8_t num_ifaces;
HAL_EventHandle _event_handle;
public:
bool add_interface(AP_HAL::CANIface *can_drv);
ICanIface* getIface(uint8_t iface_index) override;
uint8_t getNumIfaces() const override;
CanSelectMasks makeSelectMasks(const CanSelectMasks in_mask, const CanFrame* (& pending_tx)[MaxCanIfaces]) const;
int16_t select(CanSelectMasks& inout_masks,
const CanFrame* (& pending_tx)[MaxCanIfaces],
const MonotonicTime blocking_deadline) override;
};
}
#endif

View File

@ -1,90 +0,0 @@
/*
* This file is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
based on dynamic_memory.hpp which is:
Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#include <AP_HAL/AP_HAL.h>
#if HAL_ENABLE_LIBUAVCAN_DRIVERS
#include "AP_UAVCAN.h"
#include "AP_UAVCAN_pool.h"
#include <AP_InternalError/AP_InternalError.h>
AP_PoolAllocator::AP_PoolAllocator(uint16_t _pool_size) :
num_blocks(_pool_size / UAVCAN_NODE_POOL_BLOCK_SIZE)
{
}
bool AP_PoolAllocator::init(void)
{
WITH_SEMAPHORE(sem);
pool_nodes = (Node *)calloc(num_blocks, UAVCAN_NODE_POOL_BLOCK_SIZE);
if (pool_nodes == nullptr) {
return false;
}
for (uint16_t i=0; i<(num_blocks-1); i++) {
pool_nodes[i].next = &pool_nodes[i+1];
}
free_list = &pool_nodes[0];
return true;
}
void* AP_PoolAllocator::allocate(std::size_t size)
{
WITH_SEMAPHORE(sem);
if (free_list == nullptr || size > UAVCAN_NODE_POOL_BLOCK_SIZE) {
return nullptr;
}
Node *ret = free_list;
const uint32_t blk = ret - pool_nodes;
if (blk >= num_blocks) {
INTERNAL_ERROR(AP_InternalError::error_t::mem_guard);
return nullptr;
}
free_list = free_list->next;
used++;
if (used > max_used) {
max_used = used;
}
return ret;
}
void AP_PoolAllocator::deallocate(const void* ptr)
{
if (ptr == nullptr) {
return;
}
WITH_SEMAPHORE(sem);
Node *p = reinterpret_cast<Node*>(const_cast<void*>(ptr));
const uint32_t blk = p - pool_nodes;
if (blk >= num_blocks) {
INTERNAL_ERROR(AP_InternalError::error_t::mem_guard);
return;
}
p->next = free_list;
free_list = p;
used--;
}
#endif // HAL_ENABLE_LIBUAVCAN_DRIVERS

View File

@ -1,60 +0,0 @@
/*
* This file is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
based on dynamic_memory.hpp which is:
Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include "AP_UAVCAN.h"
#ifndef UAVCAN_NODE_POOL_BLOCK_SIZE
#if HAL_CANFD_SUPPORTED
#define UAVCAN_NODE_POOL_BLOCK_SIZE 128
#else
#define UAVCAN_NODE_POOL_BLOCK_SIZE 64
#endif
#endif
class AP_PoolAllocator : public uavcan::IPoolAllocator
{
public:
AP_PoolAllocator(uint16_t _pool_size);
bool init(void);
void *allocate(std::size_t size) override;
void deallocate(const void* ptr) override;
uint16_t getBlockCapacity() const override {
return num_blocks;
}
private:
const uint16_t num_blocks;
union Node {
uint8_t data[UAVCAN_NODE_POOL_BLOCK_SIZE];
Node* next;
};
Node *free_list;
Node *pool_nodes;
HAL_Semaphore sem;
uint16_t used;
uint16_t max_used;
};

View File

@ -4,42 +4,18 @@
#include <AP_Common/AP_Common.h> #include <AP_Common/AP_Common.h>
#include <AP_HAL/AP_HAL.h> #include <AP_HAL/AP_HAL.h>
#if HAL_MAX_CAN_PROTOCOL_DRIVERS #if HAL_ENABLE_LIBUAVCAN_DRIVERS
#include <AP_HAL/Semaphores.h> #include <AP_HAL/Semaphores.h>
#include <AP_UAVCAN/AP_UAVCAN.h> #include <AP_UAVCAN/AP_UAVCAN.h>
#include <uavcan/uavcan.hpp>
#include <uavcan/helpers/heap_based_pool_allocator.hpp>
#include <uavcan/equipment/gnss/Fix2.hpp>
#include <uavcan/equipment/gnss/Auxiliary.hpp>
#include <uavcan/equipment/ahrs/MagneticFieldStrength.hpp>
#include <uavcan/equipment/ahrs/MagneticFieldStrength2.hpp>
#include <uavcan/equipment/air_data/StaticPressure.hpp>
#include <uavcan/equipment/air_data/StaticTemperature.hpp>
#include <uavcan/equipment/actuator/ArrayCommand.hpp>
#include <uavcan/equipment/actuator/Command.hpp>
#include <uavcan/equipment/actuator/Status.hpp>
#include <uavcan/equipment/esc/RawCommand.hpp>
#include <uavcan/equipment/indication/LightsCommand.hpp>
#include <uavcan/equipment/indication/SingleLightCommand.hpp>
#include <uavcan/equipment/indication/RGB565.hpp>
#include <uavcan/equipment/power/BatteryInfo.hpp>
#include <com/hex/equipment/flow/Measurement.hpp>
#if CONFIG_HAL_BOARD == HAL_BOARD_LINUX #if CONFIG_HAL_BOARD == HAL_BOARD_LINUX
#include <AP_HAL_Linux/CANSocketIface.h> #include <AP_HAL_Linux/CANSocketIface.h>
#elif CONFIG_HAL_BOARD == HAL_BOARD_SITL #elif CONFIG_HAL_BOARD == HAL_BOARD_SITL
#include <AP_HAL_SITL/CANSocketIface.h> #include <AP_HAL_SITL/CANSocketIface.h>
#elif CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS #elif CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
#include <hal.h>
#include <AP_HAL_ChibiOS/CANIface.h> #include <AP_HAL_ChibiOS/CANIface.h>
#endif #endif
@ -49,10 +25,8 @@ void loop();
const AP_HAL::HAL& hal = AP_HAL::get_HAL(); const AP_HAL::HAL& hal = AP_HAL::get_HAL();
#define UAVCAN_NODE_POOL_SIZE 8192 #define UAVCAN_NODE_POOL_SIZE 8192
#ifdef UAVCAN_NODE_POOL_BLOCK_SIZE
#undef UAVCAN_NODE_POOL_BLOCK_SIZE static uint8_t node_memory_pool[UAVCAN_NODE_POOL_SIZE];
#endif
#define UAVCAN_NODE_POOL_BLOCK_SIZE 256
#define debug_uavcan(fmt, args...) do { hal.console->printf(fmt, ##args); } while (0) #define debug_uavcan(fmt, args...) do { hal.console->printf(fmt, ##args); } while (0)
@ -67,22 +41,6 @@ public:
private: private:
uint8_t driver_index = 0; uint8_t driver_index = 0;
uavcan::Node<0> *_node;
// This will be needed to implement if UAVCAN is used with multithreading
// Such cases will be firmware update, etc.
class RaiiSynchronizer {
public:
RaiiSynchronizer()
{
}
~RaiiSynchronizer()
{
}
};
uavcan::HeapBasedPoolAllocator<UAVCAN_NODE_POOL_BLOCK_SIZE, UAVCAN_sniffer::RaiiSynchronizer> _node_allocator;
AP_CANManager can_mgr; AP_CANManager can_mgr;
}; };
@ -108,20 +66,35 @@ static void count_msg(const char *name)
} }
#define MSG_CB(mtype, cbname) \ #define MSG_CB(mtype, cbname) \
static void cb_ ## cbname(const uavcan::ReceivedDataStructure<mtype>& msg) { count_msg(msg.getDataTypeFullName()); } static void cb_ ## cbname(const CanardRxTransfer &transfer, const mtype& msg) { count_msg(#mtype); }
MSG_CB(uavcan::protocol::NodeStatus, NodeStatus) MSG_CB(uavcan_protocol_NodeStatus, NodeStatus)
MSG_CB(uavcan::equipment::gnss::Fix2, Fix2) MSG_CB(uavcan_equipment_gnss_Fix2, Fix2)
MSG_CB(uavcan::equipment::gnss::Auxiliary, Auxiliary) MSG_CB(uavcan_equipment_gnss_Auxiliary, Auxiliary)
MSG_CB(uavcan::equipment::ahrs::MagneticFieldStrength, MagneticFieldStrength) MSG_CB(uavcan_equipment_ahrs_MagneticFieldStrength, MagneticFieldStrength)
MSG_CB(uavcan::equipment::ahrs::MagneticFieldStrength2, MagneticFieldStrength2); MSG_CB(uavcan_equipment_ahrs_MagneticFieldStrength2, MagneticFieldStrength2);
MSG_CB(uavcan::equipment::air_data::StaticPressure, StaticPressure) MSG_CB(uavcan_equipment_air_data_StaticPressure, StaticPressure)
MSG_CB(uavcan::equipment::air_data::StaticTemperature, StaticTemperature) MSG_CB(uavcan_equipment_air_data_StaticTemperature, StaticTemperature)
MSG_CB(uavcan::equipment::power::BatteryInfo, BatteryInfo); MSG_CB(uavcan_equipment_power_BatteryInfo, BatteryInfo);
MSG_CB(uavcan::equipment::actuator::ArrayCommand, ArrayCommand) MSG_CB(uavcan_equipment_actuator_ArrayCommand, ArrayCommand)
MSG_CB(uavcan::equipment::esc::RawCommand, RawCommand) MSG_CB(uavcan_equipment_esc_RawCommand, RawCommand)
MSG_CB(uavcan::equipment::indication::LightsCommand, LightsCommand); MSG_CB(uavcan_equipment_indication_LightsCommand, LightsCommand);
MSG_CB(com::hex::equipment::flow::Measurement, Measurement); MSG_CB(com_hex_equipment_flow_Measurement, Measurement);
uavcan_protocol_NodeStatus node_status;
uavcan_protocol_GetNodeInfoResponse node_info;
CanardInterface *_uavcan_iface_mgr;
Canard::Publisher<uavcan_protocol_NodeStatus> *node_status_pub;
Canard::Server<uavcan_protocol_GetNodeInfoRequest> *node_info_srv;
static void cb_GetNodeInfoRequest(const CanardRxTransfer &transfer, const uavcan_protocol_GetNodeInfoRequest& msg)
{
if (node_info_srv == nullptr) {
return;
}
node_info_srv->respond(transfer, node_info);
}
void UAVCAN_sniffer::init(void) void UAVCAN_sniffer::init(void)
{ {
@ -137,7 +110,7 @@ void UAVCAN_sniffer::init(void)
debug_uavcan("Can not initialised\n"); debug_uavcan("Can not initialised\n");
return; return;
} }
uavcan::CanIfaceMgr *_uavcan_iface_mgr = new uavcan::CanIfaceMgr; _uavcan_iface_mgr = new CanardInterface{driver_index};
if (_uavcan_iface_mgr == nullptr) { if (_uavcan_iface_mgr == nullptr) {
return; return;
@ -148,74 +121,63 @@ void UAVCAN_sniffer::init(void)
return; return;
} }
_node = new uavcan::Node<0>(*_uavcan_iface_mgr, uavcan::SystemClock::instance(), _node_allocator); _uavcan_iface_mgr->init(node_memory_pool, sizeof(node_memory_pool), 9);
if (_node == nullptr) { node_status_pub = new Canard::Publisher<uavcan_protocol_NodeStatus>{*_uavcan_iface_mgr};
if (node_status_pub == nullptr) {
return; return;
} }
if (_node->isStarted()) { node_info_srv = new Canard::Server<uavcan_protocol_GetNodeInfoRequest>{*_uavcan_iface_mgr, *Canard::allocate_static_callback(cb_GetNodeInfoRequest)};
if (node_info_srv == nullptr) {
return; return;
} }
uavcan::NodeID self_node_id(9); node_info.name.len = snprintf((char*)node_info.name.data, sizeof(node_info.name.data), "org.ardupilot:%u", driver_index);
_node->setNodeID(self_node_id);
char ndname[20]; node_info.software_version.major = AP_UAVCAN_SW_VERS_MAJOR;
snprintf(ndname, sizeof(ndname), "org.ardupilot:%u", driver_index); node_info.software_version.minor = AP_UAVCAN_SW_VERS_MINOR;
uavcan::NodeStatusProvider::NodeName name(ndname); node_info.hardware_version.major = AP_UAVCAN_HW_VERS_MAJOR;
_node->setName(name); node_info.hardware_version.minor = AP_UAVCAN_HW_VERS_MINOR;
uavcan::protocol::SoftwareVersion sw_version; // Standard type uavcan.protocol.SoftwareVersion #define START_CB(mtype, cbname) Canard::allocate_sub_static_callback(cb_ ## cbname,driver_index)
sw_version.major = AP_UAVCAN_SW_VERS_MAJOR;
sw_version.minor = AP_UAVCAN_SW_VERS_MINOR;
_node->setSoftwareVersion(sw_version);
uavcan::protocol::HardwareVersion hw_version; // Standard type uavcan.protocol.HardwareVersion START_CB(uavcan_protocol_NodeStatus, NodeStatus);
START_CB(uavcan_equipment_gnss_Fix2, Fix2);
hw_version.major = AP_UAVCAN_HW_VERS_MAJOR; START_CB(uavcan_equipment_gnss_Auxiliary, Auxiliary);
hw_version.minor = AP_UAVCAN_HW_VERS_MINOR; START_CB(uavcan_equipment_ahrs_MagneticFieldStrength, MagneticFieldStrength);
_node->setHardwareVersion(hw_version); START_CB(uavcan_equipment_ahrs_MagneticFieldStrength2, MagneticFieldStrength2);
START_CB(uavcan_equipment_air_data_StaticPressure, StaticPressure);
int start_res = _node->start(); START_CB(uavcan_equipment_air_data_StaticTemperature, StaticTemperature);
if (start_res < 0) { START_CB(uavcan_equipment_power_BatteryInfo, BatteryInfo);
debug_uavcan("UAVCAN: node start problem\n\r"); START_CB(uavcan_equipment_actuator_ArrayCommand, ArrayCommand);
return; START_CB(uavcan_equipment_esc_RawCommand, RawCommand);
} START_CB(uavcan_equipment_indication_LightsCommand, LightsCommand);
START_CB(com_hex_equipment_flow_Measurement, Measurement);
#define START_CB(mtype, cbname) (new uavcan::Subscriber<mtype>(*_node))->start(cb_ ## cbname)
START_CB(uavcan::protocol::NodeStatus, NodeStatus);
START_CB(uavcan::equipment::gnss::Fix2, Fix2);
START_CB(uavcan::equipment::gnss::Auxiliary, Auxiliary);
START_CB(uavcan::equipment::ahrs::MagneticFieldStrength, MagneticFieldStrength);
START_CB(uavcan::equipment::ahrs::MagneticFieldStrength2, MagneticFieldStrength2);
START_CB(uavcan::equipment::air_data::StaticPressure, StaticPressure);
START_CB(uavcan::equipment::air_data::StaticTemperature, StaticTemperature);
START_CB(uavcan::equipment::power::BatteryInfo, BatteryInfo);
START_CB(uavcan::equipment::actuator::ArrayCommand, ArrayCommand);
START_CB(uavcan::equipment::esc::RawCommand, RawCommand);
START_CB(uavcan::equipment::indication::LightsCommand, LightsCommand);
START_CB(com::hex::equipment::flow::Measurement, Measurement);
/*
* Informing other nodes that we're ready to work.
* Default mode is INITIALIZING.
*/
_node->setModeOperational();
debug_uavcan("UAVCAN: init done\n\r"); debug_uavcan("UAVCAN: init done\n\r");
} }
static void send_node_status()
{
uavcan_protocol_NodeStatus msg;
msg.uptime_sec = AP_HAL::millis() / 1000;
msg.health = UAVCAN_PROTOCOL_NODESTATUS_HEALTH_OK;
msg.mode = UAVCAN_PROTOCOL_NODESTATUS_MODE_OPERATIONAL;
msg.sub_mode = 0;
msg.vendor_specific_status_code = 0;
node_status_pub->broadcast(msg);
}
uint32_t last_status_send = 0;
void UAVCAN_sniffer::loop(void) void UAVCAN_sniffer::loop(void)
{ {
if (_node == nullptr) { if (AP_HAL::millis() - last_status_send > 1000) {
return; last_status_send = AP_HAL::millis();
send_node_status();
} }
_uavcan_iface_mgr->process(1);
_node->spin(uavcan::MonotonicDuration::fromMSec(1));
} }
void UAVCAN_sniffer::print_stats(void) void UAVCAN_sniffer::print_stats(void)
@ -233,8 +195,7 @@ void UAVCAN_sniffer::print_stats(void)
static UAVCAN_sniffer sniffer; static UAVCAN_sniffer sniffer;
UAVCAN_sniffer::UAVCAN_sniffer() : UAVCAN_sniffer::UAVCAN_sniffer()
_node_allocator(UAVCAN_NODE_POOL_SIZE, UAVCAN_NODE_POOL_SIZE)
{} {}
UAVCAN_sniffer::~UAVCAN_sniffer() UAVCAN_sniffer::~UAVCAN_sniffer()

View File

@ -1,7 +1,21 @@
#!/usr/bin/env python #!/usr/bin/env python
# encoding: utf-8 # encoding: utf-8
from waflib.TaskGen import after_method, before_method, feature
def build(bld): def build(bld):
bld.ap_example( vehicle = bld.path.name
use='ap',
bld.ap_stlib(
name=vehicle + '_libs',
ap_vehicle='UNKNOWN',
dynamic_source='modules/DroneCAN/libcanard/dsdlc_generated/src/**.c',
ap_libraries=bld.ap_common_vehicle_libraries() + [
'AP_OSD',
'AP_RCMapper',
'AP_Arming'
],
)
bld.ap_program(
program_groups=['tool'],
use=[vehicle + '_libs'],
) )