Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: UAVCAN UAVCAN_Subscriber
libuavcan_drivers/linux/include/uavcan_linux/system_utils.hpp@0:dfe6edabb8ec, 2018-04-14 (annotated)
- Committer:
- RuslanUrya
- Date:
- Sat Apr 14 10:25:32 2018 +0000
- Revision:
- 0:dfe6edabb8ec
Initial commit
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
RuslanUrya | 0:dfe6edabb8ec | 1 | /* |
RuslanUrya | 0:dfe6edabb8ec | 2 | * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com> |
RuslanUrya | 0:dfe6edabb8ec | 3 | */ |
RuslanUrya | 0:dfe6edabb8ec | 4 | |
RuslanUrya | 0:dfe6edabb8ec | 5 | #pragma once |
RuslanUrya | 0:dfe6edabb8ec | 6 | |
RuslanUrya | 0:dfe6edabb8ec | 7 | #include <array> |
RuslanUrya | 0:dfe6edabb8ec | 8 | #include <cassert> |
RuslanUrya | 0:dfe6edabb8ec | 9 | #include <cctype> |
RuslanUrya | 0:dfe6edabb8ec | 10 | #include <fstream> |
RuslanUrya | 0:dfe6edabb8ec | 11 | #include <string> |
RuslanUrya | 0:dfe6edabb8ec | 12 | #include <vector> |
RuslanUrya | 0:dfe6edabb8ec | 13 | #include <cstdint> |
RuslanUrya | 0:dfe6edabb8ec | 14 | #include <algorithm> |
RuslanUrya | 0:dfe6edabb8ec | 15 | #include <utility> |
RuslanUrya | 0:dfe6edabb8ec | 16 | #include <uavcan_linux/exception.hpp> |
RuslanUrya | 0:dfe6edabb8ec | 17 | #include <uavcan/data_type.hpp> |
RuslanUrya | 0:dfe6edabb8ec | 18 | |
RuslanUrya | 0:dfe6edabb8ec | 19 | namespace uavcan_linux |
RuslanUrya | 0:dfe6edabb8ec | 20 | { |
RuslanUrya | 0:dfe6edabb8ec | 21 | /** |
RuslanUrya | 0:dfe6edabb8ec | 22 | * This class can find and read machine ID from a text file, represented as 32-char (16-byte) long hexadecimal string, |
RuslanUrya | 0:dfe6edabb8ec | 23 | * possibly with separators (like dashes or colons). If the available ID is more than 16 bytes, extra bytes will be |
RuslanUrya | 0:dfe6edabb8ec | 24 | * ignored. A shorter ID will not be accepted as valid. |
RuslanUrya | 0:dfe6edabb8ec | 25 | * In order to be read, the ID must be located on the first line of the file and must not contain any whitespace |
RuslanUrya | 0:dfe6edabb8ec | 26 | * characters. |
RuslanUrya | 0:dfe6edabb8ec | 27 | * |
RuslanUrya | 0:dfe6edabb8ec | 28 | * Examples of valid ID: |
RuslanUrya | 0:dfe6edabb8ec | 29 | * 0123456789abcdef0123456789abcdef |
RuslanUrya | 0:dfe6edabb8ec | 30 | * 20CE0b1E-8C03-07C8-13EC-00242C491652 |
RuslanUrya | 0:dfe6edabb8ec | 31 | */ |
RuslanUrya | 0:dfe6edabb8ec | 32 | class MachineIDReader |
RuslanUrya | 0:dfe6edabb8ec | 33 | { |
RuslanUrya | 0:dfe6edabb8ec | 34 | public: |
RuslanUrya | 0:dfe6edabb8ec | 35 | static constexpr int MachineIDSize = 16; |
RuslanUrya | 0:dfe6edabb8ec | 36 | |
RuslanUrya | 0:dfe6edabb8ec | 37 | typedef std::array<std::uint8_t, MachineIDSize> MachineID; |
RuslanUrya | 0:dfe6edabb8ec | 38 | |
RuslanUrya | 0:dfe6edabb8ec | 39 | static std::vector<std::string> getDefaultSearchLocations() |
RuslanUrya | 0:dfe6edabb8ec | 40 | { |
RuslanUrya | 0:dfe6edabb8ec | 41 | return |
RuslanUrya | 0:dfe6edabb8ec | 42 | { |
RuslanUrya | 0:dfe6edabb8ec | 43 | "/etc/machine-id", |
RuslanUrya | 0:dfe6edabb8ec | 44 | "/var/lib/dbus/machine-id", |
RuslanUrya | 0:dfe6edabb8ec | 45 | "/sys/class/dmi/id/product_uuid" |
RuslanUrya | 0:dfe6edabb8ec | 46 | }; |
RuslanUrya | 0:dfe6edabb8ec | 47 | } |
RuslanUrya | 0:dfe6edabb8ec | 48 | |
RuslanUrya | 0:dfe6edabb8ec | 49 | private: |
RuslanUrya | 0:dfe6edabb8ec | 50 | const std::vector<std::string> search_locations_; |
RuslanUrya | 0:dfe6edabb8ec | 51 | |
RuslanUrya | 0:dfe6edabb8ec | 52 | static std::vector<std::string> mergeLists(const std::vector<std::string>& a, const std::vector<std::string>& b) |
RuslanUrya | 0:dfe6edabb8ec | 53 | { |
RuslanUrya | 0:dfe6edabb8ec | 54 | std::vector<std::string> ab; |
RuslanUrya | 0:dfe6edabb8ec | 55 | ab.reserve(a.size() + b.size()); |
RuslanUrya | 0:dfe6edabb8ec | 56 | ab.insert(ab.end(), a.begin(), a.end()); |
RuslanUrya | 0:dfe6edabb8ec | 57 | ab.insert(ab.end(), b.begin(), b.end()); |
RuslanUrya | 0:dfe6edabb8ec | 58 | return ab; |
RuslanUrya | 0:dfe6edabb8ec | 59 | } |
RuslanUrya | 0:dfe6edabb8ec | 60 | |
RuslanUrya | 0:dfe6edabb8ec | 61 | bool tryRead(const std::string& location, MachineID& out_id) const |
RuslanUrya | 0:dfe6edabb8ec | 62 | { |
RuslanUrya | 0:dfe6edabb8ec | 63 | /* |
RuslanUrya | 0:dfe6edabb8ec | 64 | * Reading the file |
RuslanUrya | 0:dfe6edabb8ec | 65 | */ |
RuslanUrya | 0:dfe6edabb8ec | 66 | std::string token; |
RuslanUrya | 0:dfe6edabb8ec | 67 | try |
RuslanUrya | 0:dfe6edabb8ec | 68 | { |
RuslanUrya | 0:dfe6edabb8ec | 69 | std::ifstream infile(location); |
RuslanUrya | 0:dfe6edabb8ec | 70 | infile >> token; |
RuslanUrya | 0:dfe6edabb8ec | 71 | } |
RuslanUrya | 0:dfe6edabb8ec | 72 | catch (std::exception&) |
RuslanUrya | 0:dfe6edabb8ec | 73 | { |
RuslanUrya | 0:dfe6edabb8ec | 74 | return false; |
RuslanUrya | 0:dfe6edabb8ec | 75 | } |
RuslanUrya | 0:dfe6edabb8ec | 76 | |
RuslanUrya | 0:dfe6edabb8ec | 77 | /* |
RuslanUrya | 0:dfe6edabb8ec | 78 | * Preprocessing the input string - convert to lowercase, remove all non-hex characters, limit to 32 chars |
RuslanUrya | 0:dfe6edabb8ec | 79 | */ |
RuslanUrya | 0:dfe6edabb8ec | 80 | std::transform(token.begin(), token.end(), token.begin(), [](char x) { return std::tolower(x); }); |
RuslanUrya | 0:dfe6edabb8ec | 81 | token.erase(std::remove_if(token.begin(), token.end(), |
RuslanUrya | 0:dfe6edabb8ec | 82 | [](char x){ return (x < 'a' || x > 'f') && !std::isdigit(x); }), |
RuslanUrya | 0:dfe6edabb8ec | 83 | token.end()); |
RuslanUrya | 0:dfe6edabb8ec | 84 | |
RuslanUrya | 0:dfe6edabb8ec | 85 | if (token.length() < (MachineIDSize * 2)) |
RuslanUrya | 0:dfe6edabb8ec | 86 | { |
RuslanUrya | 0:dfe6edabb8ec | 87 | return false; |
RuslanUrya | 0:dfe6edabb8ec | 88 | } |
RuslanUrya | 0:dfe6edabb8ec | 89 | token.resize(MachineIDSize * 2); // Truncating |
RuslanUrya | 0:dfe6edabb8ec | 90 | |
RuslanUrya | 0:dfe6edabb8ec | 91 | /* |
RuslanUrya | 0:dfe6edabb8ec | 92 | * Parsing the string as hex bytes |
RuslanUrya | 0:dfe6edabb8ec | 93 | */ |
RuslanUrya | 0:dfe6edabb8ec | 94 | auto sym = std::begin(token); |
RuslanUrya | 0:dfe6edabb8ec | 95 | for (auto& byte : out_id) |
RuslanUrya | 0:dfe6edabb8ec | 96 | { |
RuslanUrya | 0:dfe6edabb8ec | 97 | assert(sym != std::end(token)); |
RuslanUrya | 0:dfe6edabb8ec | 98 | byte = std::stoi(std::string{*sym++, *sym++}, nullptr, 16); |
RuslanUrya | 0:dfe6edabb8ec | 99 | } |
RuslanUrya | 0:dfe6edabb8ec | 100 | |
RuslanUrya | 0:dfe6edabb8ec | 101 | return true; |
RuslanUrya | 0:dfe6edabb8ec | 102 | } |
RuslanUrya | 0:dfe6edabb8ec | 103 | |
RuslanUrya | 0:dfe6edabb8ec | 104 | public: |
RuslanUrya | 0:dfe6edabb8ec | 105 | /** |
RuslanUrya | 0:dfe6edabb8ec | 106 | * This class can use extra seach locations. If provided, they will be checked first, before default ones. |
RuslanUrya | 0:dfe6edabb8ec | 107 | */ |
RuslanUrya | 0:dfe6edabb8ec | 108 | MachineIDReader(const std::vector<std::string>& extra_search_locations = {}) |
RuslanUrya | 0:dfe6edabb8ec | 109 | : search_locations_(mergeLists(extra_search_locations, getDefaultSearchLocations())) |
RuslanUrya | 0:dfe6edabb8ec | 110 | { } |
RuslanUrya | 0:dfe6edabb8ec | 111 | |
RuslanUrya | 0:dfe6edabb8ec | 112 | /** |
RuslanUrya | 0:dfe6edabb8ec | 113 | * Just like @ref readAndGetLocation(), but this one doesn't return location where this ID was obtained from. |
RuslanUrya | 0:dfe6edabb8ec | 114 | */ |
RuslanUrya | 0:dfe6edabb8ec | 115 | MachineID read() const { return readAndGetLocation().first; } |
RuslanUrya | 0:dfe6edabb8ec | 116 | |
RuslanUrya | 0:dfe6edabb8ec | 117 | /** |
RuslanUrya | 0:dfe6edabb8ec | 118 | * This function checks available search locations and reads the ID from the first valid location. |
RuslanUrya | 0:dfe6edabb8ec | 119 | * It returns std::pair<> with ID and the file path where it was read from. |
RuslanUrya | 0:dfe6edabb8ec | 120 | * In case if none of the search locations turned out to be valid, @ref uavcan_linux::Exception will be thrown. |
RuslanUrya | 0:dfe6edabb8ec | 121 | */ |
RuslanUrya | 0:dfe6edabb8ec | 122 | std::pair<MachineID, std::string> readAndGetLocation() const |
RuslanUrya | 0:dfe6edabb8ec | 123 | { |
RuslanUrya | 0:dfe6edabb8ec | 124 | for (auto x : search_locations_) |
RuslanUrya | 0:dfe6edabb8ec | 125 | { |
RuslanUrya | 0:dfe6edabb8ec | 126 | auto out = MachineID(); |
RuslanUrya | 0:dfe6edabb8ec | 127 | if (tryRead(x, out)) |
RuslanUrya | 0:dfe6edabb8ec | 128 | { |
RuslanUrya | 0:dfe6edabb8ec | 129 | return {out, x}; |
RuslanUrya | 0:dfe6edabb8ec | 130 | } |
RuslanUrya | 0:dfe6edabb8ec | 131 | } |
RuslanUrya | 0:dfe6edabb8ec | 132 | throw Exception("Failed to read machine ID"); |
RuslanUrya | 0:dfe6edabb8ec | 133 | } |
RuslanUrya | 0:dfe6edabb8ec | 134 | }; |
RuslanUrya | 0:dfe6edabb8ec | 135 | |
RuslanUrya | 0:dfe6edabb8ec | 136 | /** |
RuslanUrya | 0:dfe6edabb8ec | 137 | * This class computes unique ID for a UAVCAN node in a Linux application. |
RuslanUrya | 0:dfe6edabb8ec | 138 | * It takes the following inputs: |
RuslanUrya | 0:dfe6edabb8ec | 139 | * - Unique machine ID |
RuslanUrya | 0:dfe6edabb8ec | 140 | * - Node name string (e.g. "org.uavcan.linux_app.dynamic_node_id_server") |
RuslanUrya | 0:dfe6edabb8ec | 141 | * - Instance ID byte, e.g. node ID (optional) |
RuslanUrya | 0:dfe6edabb8ec | 142 | */ |
RuslanUrya | 0:dfe6edabb8ec | 143 | inline std::array<std::uint8_t, 16> makeApplicationID(const MachineIDReader::MachineID& machine_id, |
RuslanUrya | 0:dfe6edabb8ec | 144 | const std::string& node_name, |
RuslanUrya | 0:dfe6edabb8ec | 145 | const std::uint8_t instance_id = 0) |
RuslanUrya | 0:dfe6edabb8ec | 146 | { |
RuslanUrya | 0:dfe6edabb8ec | 147 | union HalfID |
RuslanUrya | 0:dfe6edabb8ec | 148 | { |
RuslanUrya | 0:dfe6edabb8ec | 149 | std::uint64_t num; |
RuslanUrya | 0:dfe6edabb8ec | 150 | std::uint8_t bytes[8]; |
RuslanUrya | 0:dfe6edabb8ec | 151 | |
RuslanUrya | 0:dfe6edabb8ec | 152 | HalfID(std::uint64_t arg_num) : num(arg_num) { } |
RuslanUrya | 0:dfe6edabb8ec | 153 | }; |
RuslanUrya | 0:dfe6edabb8ec | 154 | |
RuslanUrya | 0:dfe6edabb8ec | 155 | std::array<std::uint8_t, 16> out; |
RuslanUrya | 0:dfe6edabb8ec | 156 | |
RuslanUrya | 0:dfe6edabb8ec | 157 | // First 8 bytes of the application ID are CRC64 of the machine ID in native byte order |
RuslanUrya | 0:dfe6edabb8ec | 158 | { |
RuslanUrya | 0:dfe6edabb8ec | 159 | uavcan::DataTypeSignatureCRC crc; |
RuslanUrya | 0:dfe6edabb8ec | 160 | crc.add(machine_id.data(), static_cast<unsigned>(machine_id.size())); |
RuslanUrya | 0:dfe6edabb8ec | 161 | HalfID half(crc.get()); |
RuslanUrya | 0:dfe6edabb8ec | 162 | std::copy_n(half.bytes, 8, out.begin()); |
RuslanUrya | 0:dfe6edabb8ec | 163 | } |
RuslanUrya | 0:dfe6edabb8ec | 164 | |
RuslanUrya | 0:dfe6edabb8ec | 165 | // Last 8 bytes of the application ID are CRC64 of the node name and optionally node ID |
RuslanUrya | 0:dfe6edabb8ec | 166 | { |
RuslanUrya | 0:dfe6edabb8ec | 167 | uavcan::DataTypeSignatureCRC crc; |
RuslanUrya | 0:dfe6edabb8ec | 168 | crc.add(reinterpret_cast<const std::uint8_t*>(node_name.c_str()), static_cast<unsigned>(node_name.length())); |
RuslanUrya | 0:dfe6edabb8ec | 169 | crc.add(instance_id); |
RuslanUrya | 0:dfe6edabb8ec | 170 | HalfID half(crc.get()); |
RuslanUrya | 0:dfe6edabb8ec | 171 | std::copy_n(half.bytes, 8, out.begin() + 8); |
RuslanUrya | 0:dfe6edabb8ec | 172 | } |
RuslanUrya | 0:dfe6edabb8ec | 173 | |
RuslanUrya | 0:dfe6edabb8ec | 174 | return out; |
RuslanUrya | 0:dfe6edabb8ec | 175 | } |
RuslanUrya | 0:dfe6edabb8ec | 176 | |
RuslanUrya | 0:dfe6edabb8ec | 177 | } |