Руслан Урядинский / libuavcan

Dependents:   UAVCAN UAVCAN_Subscriber

Committer:
RuslanUrya
Date:
Sat Apr 14 10:25:32 2018 +0000
Revision:
0:dfe6edabb8ec
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew 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 }