/* * 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 . * * Author: Siddharth Bharat Purohit */ #include #if HAL_WITH_UAVCAN #include "AP_UAVCAN_Servers.h" #ifdef HAS_UAVCAN_SERVERS #include #include #include #include #include #include #include #include #include #ifndef UAVCAN_NODE_DB_PATH #define UAVCAN_NODE_DB_PATH HAL_BOARD_STORAGE_DIRECTORY "/UAVCAN" #endif #include extern const AP_HAL::HAL& hal; #define debug_uavcan(fmt, args...) do { hal.console->printf(fmt, ##args); } while (0) class AP_UAVCAN_CentralizedServer : public uavcan::dynamic_node_id_server::CentralizedServer { public: AP_UAVCAN_CentralizedServer(uavcan::INode& node, uavcan::dynamic_node_id_server::IStorageBackend &storage_backend, uavcan::dynamic_node_id_server::IEventTracer &tracer) : uavcan::dynamic_node_id_server::CentralizedServer(node, storage_backend, tracer) {} }; class AP_UAVCAN_FileEventTracer : public uavcan::dynamic_node_id_server::IEventTracer { protected: virtual void onEvent(uavcan::dynamic_node_id_server::TraceCode code, uavcan::int64_t argument) override { AP::logger().Write("UCEV", "TimeUS,code,arg", "s--", "F--", "Qhq", AP_HAL::micros64(), code, argument); } }; class AP_UAVCAN_RestartRequestHandler : public uavcan::IRestartRequestHandler { public: bool handleRestartRequest(uavcan::NodeID request_source) override { // swiped from reboot handling in GCS_Common.cpp if (hal.util->get_soft_armed()) { // refuse reboot when armed return false; } AP_Notify *notify = AP_Notify::get_singleton(); if (notify) { AP_Notify::flags.firmware_update = 1; notify->update(); } // force safety on hal.rcout->force_safety_on(); // flush pending parameter writes AP_Param::flush(); hal.scheduler->delay(200); hal.scheduler->reboot(false); return true; } }; class AP_UAVCAN_FileStorageBackend : public uavcan::dynamic_node_id_server::IStorageBackend { /** * Maximum length of full path including / and key max */ enum { MaxPathLength = 128 }; enum { MaxNumOpens = 100 }; /** * This type is used for the path */ typedef uavcan::MakeString::Type PathString; PathString base_path; static uint8_t num_opens; protected: virtual String get(const String& key) const override { using namespace std; PathString path = base_path.c_str(); path += key; String value; //This is to deter frequent inflight opening and closing of files during an event //where the device is misbehaving if (num_opens >= MaxNumOpens) { return value; } num_opens++; int fd = AP::FS().open(path.c_str(), O_RDONLY); if (fd >= 0) { char buffer[MaxStringLength + 1]; (void)memset(buffer, 0, sizeof(buffer)); ssize_t remaining = MaxStringLength; ssize_t total_read = 0; ssize_t nread = 0; do { nread = AP::FS().read(fd, &buffer[total_read], remaining); if (nread > 0) { remaining -= nread, total_read += nread; } } while (nread > 0 && remaining > 0); AP::FS().close(fd); if (total_read > 0) { for (int i = 0; i < total_read; i++) { if (buffer[i] == ' ' || buffer[i] == '\n' || buffer[i] == '\r') { buffer[i] = '\0'; break; } } value = buffer; } } return value; } virtual void set(const String& key, const String& value) override { using namespace std; PathString path = base_path.c_str(); path += key; //This is to deter frequent inflight opening and closing of files during an event //where the device is misbehaving if (num_opens >= MaxNumOpens) { return; } num_opens++; int fd = AP::FS().open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC); if (fd >= 0) { ssize_t remaining = value.size(); ssize_t total_written = 0; ssize_t written = 0; do { written = AP::FS().write(fd, &value.c_str()[total_written], remaining); if (written > 0) { total_written += written; remaining -= written; } } while (written > 0 && remaining > 0); AP::FS().fsync(fd); AP::FS().close(fd); } } public: /** * Initializes the file based backend storage by passing a path to * the directory where the key named files will be stored. * The return value should be 0 on success. * If it is -ErrInvalidConfiguration then the the path name is too long to * accommodate the trailing slash and max key length. */ int init(const PathString& path) { using namespace std; int rv = -uavcan::ErrInvalidParam; if (path.size() > 0) { base_path = path.c_str(); if (base_path.back() == '/') { base_path.pop_back(); } rv = 0; struct stat sb; if (AP::FS().stat(base_path.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) { rv = AP::FS().mkdir(base_path.c_str()); } if (rv >= 0) { base_path.push_back('/'); if ((base_path.size() + MaxStringLength) > MaxPathLength) { rv = -uavcan::ErrInvalidConfiguration; } } } return rv; } }; uint8_t AP_UAVCAN_FileStorageBackend::num_opens = 0; bool AP_UAVCAN_Servers::init(uavcan::Node<0> &node) { if (_server_instance != nullptr) { return true; } int ret = 0; _storage_backend = new AP_UAVCAN_FileStorageBackend; if (_storage_backend == nullptr) { debug_uavcan("UAVCAN_Servers: Failed to Allocate FileStorageBackend\n"); goto failed; } ret = _storage_backend->init(UAVCAN_NODE_DB_PATH); if (ret < 0) { debug_uavcan("UAVCAN_Servers: FileStorageBackend init: %d, errno: %d\n", ret, errno); goto failed; } _tracer = new AP_UAVCAN_FileEventTracer; if (_tracer == nullptr) { debug_uavcan("UAVCAN_Servers: Failed to Allocate FileEventTracer\n"); goto failed; } _server_instance = new AP_UAVCAN_CentralizedServer(node, *_storage_backend, *_tracer); if (_server_instance == nullptr) { debug_uavcan("UAVCAN_Servers: Failed to Allocate Server\n"); goto failed; } { uavcan::dynamic_node_id_server::centralized::Storage storage(*_storage_backend); if (storage.getNodeIDForUniqueID(node.getHardwareVersion().unique_id) != node.getNodeID()) { //Node ID was changed, reseting database reset(); } } if (_restart_request_handler == nullptr) { _restart_request_handler = new AP_UAVCAN_RestartRequestHandler(); if (_restart_request_handler == nullptr) { goto failed; } } node.setRestartRequestHandler(_restart_request_handler); //Start Dynamic Node Server ret = _server_instance->init(node.getHardwareVersion().unique_id); if (ret < 0) { debug_uavcan("UAVCAN_Server init: %d, errno: %d\n", ret, errno); goto failed; } return true; failed: delete _restart_request_handler; delete _storage_backend; delete _tracer; delete _server_instance; return false; } void AP_UAVCAN_Servers::reset() { debug_uavcan("UAVCAN_Servers: Resetting Server Database...\n"); DIR* dp; struct dirent* ep; dp = AP::FS().opendir(UAVCAN_NODE_DB_PATH); char abs_filename[100]; if (dp != NULL) { while((ep = AP::FS().readdir(dp))) { snprintf(abs_filename, 100, "%s/%s", UAVCAN_NODE_DB_PATH, ep->d_name); AP::FS().unlink(abs_filename); } } AP::FS().closedir(dp); } #endif #endif //HAL_WITH_UAVCAN