├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── ext ├── doctest.h ├── fmt │ ├── fmtlib.cc │ └── fmtlib.h ├── gsl │ ├── gsl │ ├── gsl_algorithm │ ├── gsl_assert │ ├── gsl_byte │ ├── gsl_util │ ├── multi_span │ ├── span │ └── string_span └── json.hpp ├── src ├── file │ ├── dbcfile.h │ ├── dbcmetaparser.cpp │ ├── dbcmetaparser.h │ ├── dbcparser.cpp │ ├── dbcparser.h │ ├── dbcwriter.cpp │ ├── dbcwriter.h │ ├── jsonreader.cpp │ ├── jsonreader.h │ ├── jsonwriter.cpp │ └── jsonwriter.h ├── main.cpp ├── models │ ├── canbusdefmodel.cpp │ ├── canbusdefmodel.h │ ├── canbusmodel.cpp │ ├── canbusmodel.h │ ├── canframedefitem.cpp │ ├── canframedefitem.h │ ├── canframeitem.cpp │ ├── canframeitem.h │ ├── cansignaldefitem.cpp │ ├── cansignaldefitem.h │ ├── cansignalitem.cpp │ ├── cansignalitem.h │ ├── treeitem.cpp │ ├── treeitem.h │ ├── treeitemid.h │ ├── treemodel.cpp │ ├── treemodel.h │ ├── valuedefitem.cpp │ ├── valuedefitem.h │ ├── valuedefmodel.cpp │ └── valuedefmodel.h ├── network │ ├── canudpreceiver.cpp │ ├── canudpreceiver.h │ ├── dummyreceiver.h │ ├── udpasyncreceiver.cpp │ ├── udpasyncreceiver.h │ └── udpsocketguard.h ├── tincan │ ├── canbus.cpp │ ├── canbus.h │ ├── canbusdef.h │ ├── canframe.h │ ├── canframedef.h │ ├── canrawframe.h │ ├── cansignal.h │ ├── cansignaldef.h │ ├── cantracer.cpp │ ├── cantracer.h │ ├── errors.h │ ├── helper.h │ ├── signalutil.cpp │ ├── signalutil.h │ ├── simulator.cpp │ ├── simulator.h │ ├── translate.cpp │ └── translate.h ├── ui │ ├── mainwindow.cpp │ ├── mainwindow.h │ ├── treeviewdialog.cpp │ └── treeviewdialog.h ├── util.cpp └── util.h ├── test ├── dbcparser_test.cpp ├── makefile ├── signalutil_test.cpp └── test.dbc ├── tincan.png ├── tincan.pro ├── tincan_treeview.png └── ui ├── mainwindow.ui └── treeviewdialog.ui /.gitattributes: -------------------------------------------------------------------------------- 1 | # Default behavior 2 | * text eol=lf 3 | 4 | # Explicitly declare text files 5 | *.c text 6 | *.cc text 7 | *.cpp text 8 | *.h text 9 | *.hh text 10 | *.hpp text 11 | *.pro text 12 | *.qrc text 13 | *.conf text 14 | *.md text 15 | 16 | # Denote all files that are truly binary and should not be modified 17 | *.png binary 18 | *.jpg binary 19 | *.ico binary 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Project files 35 | *.user 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Peter Ebermann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tincan 2 |

tincan banner

3 |

Visualization of CAN bus traffic via UDP interface

4 | 5 | Description 6 | --- 7 | Tincan decodes and displays CAN frames. The default interface is via UDP socket and requires a gateway device, e.g. a Raspberry Pi 3 with a PiCAN2 HAT running [cangw](https://github.com/mwkpe/cantools), or any CAN to UDP gateway that sends raw frames. Adding other interfaces can be done by adapting the [dummy receiver](src/network/dummyreceiver.h). 8 | 9 | Features 10 | --- 11 | * Import of frame, signal and value definitions from DBC files 12 | * Live view of received frames and signals in a [tree view](tincan_treeview.png). 13 | * Simple trace view of frame and signal data 14 | 15 | Status 16 | --- 17 | WIP 18 | 19 | Build 20 | --- 21 | Building requires the Qt framework (Qt 5.12+ MinGW 7.3). The project file must be modified where absolute paths to boost and Asio were used. The other libs are part of the repo. 22 | 23 | Acknowledgements 24 | --- 25 | Tincan is using [Qt](http://https://www.qt.io), [boost](http://www.boost.org/), [Spirit X3](http://boost-spirit.com), [Asio](https://think-async.com/), [GSL](https://github.com/Microsoft/GSL), [fmt](https://github.com/fmtlib/fmt), [JSON](https://github.com/nlohmann/json) and [doctest](https://github.com/onqtam/doctest). 26 | -------------------------------------------------------------------------------- /ext/fmt/fmtlib.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Formatting library for C++ 3 | 4 | Copyright (c) 2012 - 2016, Victor Zverovich 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include "fmtlib.h" 29 | 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include // for std::ptrdiff_t 38 | 39 | #if defined(_WIN32) && defined(__MINGW32__) 40 | # include 41 | #endif 42 | 43 | #if FMT_USE_WINDOWS_H 44 | # if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN) 45 | # define WIN32_LEAN_AND_MEAN 46 | # endif 47 | # if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) 48 | # include 49 | # else 50 | # define NOMINMAX 51 | # include 52 | # undef NOMINMAX 53 | # endif 54 | #endif 55 | 56 | #if FMT_EXCEPTIONS 57 | # define FMT_TRY try 58 | # define FMT_CATCH(x) catch (x) 59 | #else 60 | # define FMT_TRY if (true) 61 | # define FMT_CATCH(x) if (false) 62 | #endif 63 | 64 | #ifdef _MSC_VER 65 | # pragma warning(push) 66 | # pragma warning(disable: 4127) // conditional expression is constant 67 | # pragma warning(disable: 4702) // unreachable code 68 | // Disable deprecation warning for strerror. The latter is not called but 69 | // MSVC fails to detect it. 70 | # pragma warning(disable: 4996) 71 | #endif 72 | 73 | // Dummy implementations of strerror_r and strerror_s called if corresponding 74 | // system functions are not available. 75 | static inline fmt::internal::Null<> strerror_r(int, char *, ...) { 76 | return fmt::internal::Null<>(); 77 | } 78 | static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { 79 | return fmt::internal::Null<>(); 80 | } 81 | 82 | namespace fmt { 83 | 84 | FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {} 85 | FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {} 86 | FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {} 87 | 88 | namespace { 89 | 90 | #ifndef _MSC_VER 91 | # define FMT_SNPRINTF snprintf 92 | #else // _MSC_VER 93 | inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { 94 | va_list args; 95 | va_start(args, format); 96 | int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); 97 | va_end(args); 98 | return result; 99 | } 100 | # define FMT_SNPRINTF fmt_snprintf 101 | #endif // _MSC_VER 102 | 103 | #if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) 104 | # define FMT_SWPRINTF snwprintf 105 | #else 106 | # define FMT_SWPRINTF swprintf 107 | #endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) 108 | 109 | const char RESET_COLOR[] = "\x1b[0m"; 110 | 111 | typedef void (*FormatFunc)(Writer &, int, StringRef); 112 | 113 | // Portable thread-safe version of strerror. 114 | // Sets buffer to point to a string describing the error code. 115 | // This can be either a pointer to a string stored in buffer, 116 | // or a pointer to some static immutable string. 117 | // Returns one of the following values: 118 | // 0 - success 119 | // ERANGE - buffer is not large enough to store the error message 120 | // other - failure 121 | // Buffer should be at least of size 1. 122 | int safe_strerror( 123 | int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { 124 | FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer"); 125 | 126 | class StrError { 127 | private: 128 | int error_code_; 129 | char *&buffer_; 130 | std::size_t buffer_size_; 131 | 132 | // A noop assignment operator to avoid bogus warnings. 133 | void operator=(const StrError &) {} 134 | 135 | // Handle the result of XSI-compliant version of strerror_r. 136 | int handle(int result) { 137 | // glibc versions before 2.13 return result in errno. 138 | return result == -1 ? errno : result; 139 | } 140 | 141 | // Handle the result of GNU-specific version of strerror_r. 142 | int handle(char *message) { 143 | // If the buffer is full then the message is probably truncated. 144 | if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) 145 | return ERANGE; 146 | buffer_ = message; 147 | return 0; 148 | } 149 | 150 | // Handle the case when strerror_r is not available. 151 | int handle(internal::Null<>) { 152 | return fallback(strerror_s(buffer_, buffer_size_, error_code_)); 153 | } 154 | 155 | // Fallback to strerror_s when strerror_r is not available. 156 | int fallback(int result) { 157 | // If the buffer is full then the message is probably truncated. 158 | return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? 159 | ERANGE : result; 160 | } 161 | 162 | // Fallback to strerror if strerror_r and strerror_s are not available. 163 | int fallback(internal::Null<>) { 164 | errno = 0; 165 | buffer_ = strerror(error_code_); 166 | return errno; 167 | } 168 | 169 | public: 170 | StrError(int err_code, char *&buf, std::size_t buf_size) 171 | : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} 172 | 173 | int run() { 174 | // Suppress a warning about unused strerror_r. 175 | strerror_r(0, FMT_NULL, ""); 176 | return handle(strerror_r(error_code_, buffer_, buffer_size_)); 177 | } 178 | }; 179 | return StrError(error_code, buffer, buffer_size).run(); 180 | } 181 | 182 | void format_error_code(Writer &out, int error_code, 183 | StringRef message) FMT_NOEXCEPT { 184 | // Report error code making sure that the output fits into 185 | // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential 186 | // bad_alloc. 187 | out.clear(); 188 | static const char SEP[] = ": "; 189 | static const char ERROR_STR[] = "error "; 190 | // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. 191 | std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; 192 | typedef internal::IntTraits::MainType MainType; 193 | MainType abs_value = static_cast(error_code); 194 | if (internal::is_negative(error_code)) { 195 | abs_value = 0 - abs_value; 196 | ++error_code_size; 197 | } 198 | error_code_size += internal::count_digits(abs_value); 199 | if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size) 200 | out << message << SEP; 201 | out << ERROR_STR << error_code; 202 | assert(out.size() <= internal::INLINE_BUFFER_SIZE); 203 | } 204 | 205 | void report_error(FormatFunc func, int error_code, 206 | StringRef message) FMT_NOEXCEPT { 207 | MemoryWriter full_message; 208 | func(full_message, error_code, message); 209 | // Use Writer::data instead of Writer::c_str to avoid potential memory 210 | // allocation. 211 | std::fwrite(full_message.data(), full_message.size(), 1, stderr); 212 | std::fputc('\n', stderr); 213 | } 214 | } // namespace 215 | 216 | FMT_FUNC void SystemError::init( 217 | int err_code, CStringRef format_str, ArgList args) { 218 | error_code_ = err_code; 219 | MemoryWriter w; 220 | format_system_error(w, err_code, format(format_str, args)); 221 | std::runtime_error &base = *this; 222 | base = std::runtime_error(w.str()); 223 | } 224 | 225 | template 226 | int internal::CharTraits::format_float( 227 | char *buffer, std::size_t size, const char *format, 228 | unsigned width, int precision, T value) { 229 | if (width == 0) { 230 | return precision < 0 ? 231 | FMT_SNPRINTF(buffer, size, format, value) : 232 | FMT_SNPRINTF(buffer, size, format, precision, value); 233 | } 234 | return precision < 0 ? 235 | FMT_SNPRINTF(buffer, size, format, width, value) : 236 | FMT_SNPRINTF(buffer, size, format, width, precision, value); 237 | } 238 | 239 | template 240 | int internal::CharTraits::format_float( 241 | wchar_t *buffer, std::size_t size, const wchar_t *format, 242 | unsigned width, int precision, T value) { 243 | if (width == 0) { 244 | return precision < 0 ? 245 | FMT_SWPRINTF(buffer, size, format, value) : 246 | FMT_SWPRINTF(buffer, size, format, precision, value); 247 | } 248 | return precision < 0 ? 249 | FMT_SWPRINTF(buffer, size, format, width, value) : 250 | FMT_SWPRINTF(buffer, size, format, width, precision, value); 251 | } 252 | 253 | template 254 | const char internal::BasicData::DIGITS[] = 255 | "0001020304050607080910111213141516171819" 256 | "2021222324252627282930313233343536373839" 257 | "4041424344454647484950515253545556575859" 258 | "6061626364656667686970717273747576777879" 259 | "8081828384858687888990919293949596979899"; 260 | 261 | #define FMT_POWERS_OF_10(factor) \ 262 | factor * 10, \ 263 | factor * 100, \ 264 | factor * 1000, \ 265 | factor * 10000, \ 266 | factor * 100000, \ 267 | factor * 1000000, \ 268 | factor * 10000000, \ 269 | factor * 100000000, \ 270 | factor * 1000000000 271 | 272 | template 273 | const uint32_t internal::BasicData::POWERS_OF_10_32[] = { 274 | 0, FMT_POWERS_OF_10(1) 275 | }; 276 | 277 | template 278 | const uint64_t internal::BasicData::POWERS_OF_10_64[] = { 279 | 0, 280 | FMT_POWERS_OF_10(1), 281 | FMT_POWERS_OF_10(ULongLong(1000000000)), 282 | // Multiply several constants instead of using a single long long constant 283 | // to avoid warnings about C++98 not supporting long long. 284 | ULongLong(1000000000) * ULongLong(1000000000) * 10 285 | }; 286 | 287 | FMT_FUNC void internal::report_unknown_type(char code, const char *type) { 288 | (void)type; 289 | if (std::isprint(static_cast(code))) { 290 | FMT_THROW(FormatError( 291 | format("unknown format code '{}' for {}", code, type))); 292 | } 293 | FMT_THROW(FormatError( 294 | format("unknown format code '\\x{:02x}' for {}", 295 | static_cast(code), type))); 296 | } 297 | 298 | #if FMT_USE_WINDOWS_H 299 | 300 | FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) { 301 | static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; 302 | if (s.size() > INT_MAX) 303 | FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG)); 304 | int s_size = static_cast(s.size()); 305 | int length = MultiByteToWideChar( 306 | CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0); 307 | if (length == 0) 308 | FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); 309 | buffer_.resize(length + 1); 310 | length = MultiByteToWideChar( 311 | CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length); 312 | if (length == 0) 313 | FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); 314 | buffer_[length] = 0; 315 | } 316 | 317 | FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) { 318 | if (int error_code = convert(s)) { 319 | FMT_THROW(WindowsError(error_code, 320 | "cannot convert string from UTF-16 to UTF-8")); 321 | } 322 | } 323 | 324 | FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) { 325 | if (s.size() > INT_MAX) 326 | return ERROR_INVALID_PARAMETER; 327 | int s_size = static_cast(s.size()); 328 | int length = WideCharToMultiByte( 329 | CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL); 330 | if (length == 0) 331 | return GetLastError(); 332 | buffer_.resize(length + 1); 333 | length = WideCharToMultiByte( 334 | CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL); 335 | if (length == 0) 336 | return GetLastError(); 337 | buffer_[length] = 0; 338 | return 0; 339 | } 340 | 341 | FMT_FUNC void WindowsError::init( 342 | int err_code, CStringRef format_str, ArgList args) { 343 | error_code_ = err_code; 344 | MemoryWriter w; 345 | internal::format_windows_error(w, err_code, format(format_str, args)); 346 | std::runtime_error &base = *this; 347 | base = std::runtime_error(w.str()); 348 | } 349 | 350 | FMT_FUNC void internal::format_windows_error( 351 | Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { 352 | FMT_TRY { 353 | MemoryBuffer buffer; 354 | buffer.resize(INLINE_BUFFER_SIZE); 355 | for (;;) { 356 | wchar_t *system_message = &buffer[0]; 357 | int result = FormatMessageW( 358 | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 359 | FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 360 | system_message, static_cast(buffer.size()), FMT_NULL); 361 | if (result != 0) { 362 | UTF16ToUTF8 utf8_message; 363 | if (utf8_message.convert(system_message) == ERROR_SUCCESS) { 364 | out << message << ": " << utf8_message; 365 | return; 366 | } 367 | break; 368 | } 369 | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 370 | break; // Can't get error message, report error code instead. 371 | buffer.resize(buffer.size() * 2); 372 | } 373 | } FMT_CATCH(...) {} 374 | fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. 375 | } 376 | 377 | #endif // FMT_USE_WINDOWS_H 378 | 379 | FMT_FUNC void format_system_error( 380 | Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { 381 | FMT_TRY { 382 | internal::MemoryBuffer buffer; 383 | buffer.resize(internal::INLINE_BUFFER_SIZE); 384 | for (;;) { 385 | char *system_message = &buffer[0]; 386 | int result = safe_strerror(error_code, system_message, buffer.size()); 387 | if (result == 0) { 388 | out << message << ": " << system_message; 389 | return; 390 | } 391 | if (result != ERANGE) 392 | break; // Can't get error message, report error code instead. 393 | buffer.resize(buffer.size() * 2); 394 | } 395 | } FMT_CATCH(...) {} 396 | fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. 397 | } 398 | 399 | template 400 | void internal::ArgMap::init(const ArgList &args) { 401 | if (!map_.empty()) 402 | return; 403 | typedef internal::NamedArg NamedArg; 404 | const NamedArg *named_arg = FMT_NULL; 405 | bool use_values = 406 | args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; 407 | if (use_values) { 408 | for (unsigned i = 0;/*nothing*/; ++i) { 409 | internal::Arg::Type arg_type = args.type(i); 410 | switch (arg_type) { 411 | case internal::Arg::NONE: 412 | return; 413 | case internal::Arg::NAMED_ARG: 414 | named_arg = static_cast(args.values_[i].pointer); 415 | map_.push_back(Pair(named_arg->name, *named_arg)); 416 | break; 417 | default: 418 | /*nothing*/; 419 | } 420 | } 421 | return; 422 | } 423 | for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) { 424 | internal::Arg::Type arg_type = args.type(i); 425 | if (arg_type == internal::Arg::NAMED_ARG) { 426 | named_arg = static_cast(args.args_[i].pointer); 427 | map_.push_back(Pair(named_arg->name, *named_arg)); 428 | } 429 | } 430 | for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) { 431 | switch (args.args_[i].type) { 432 | case internal::Arg::NONE: 433 | return; 434 | case internal::Arg::NAMED_ARG: 435 | named_arg = static_cast(args.args_[i].pointer); 436 | map_.push_back(Pair(named_arg->name, *named_arg)); 437 | break; 438 | default: 439 | /*nothing*/; 440 | } 441 | } 442 | } 443 | 444 | template 445 | void internal::FixedBuffer::grow(std::size_t) { 446 | FMT_THROW(std::runtime_error("buffer overflow")); 447 | } 448 | 449 | FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg( 450 | unsigned arg_index, const char *&error) { 451 | internal::Arg arg = args_[arg_index]; 452 | switch (arg.type) { 453 | case internal::Arg::NONE: 454 | error = "argument index out of range"; 455 | break; 456 | case internal::Arg::NAMED_ARG: 457 | arg = *static_cast(arg.pointer); 458 | break; 459 | default: 460 | /*nothing*/; 461 | } 462 | return arg; 463 | } 464 | 465 | FMT_FUNC void report_system_error( 466 | int error_code, fmt::StringRef message) FMT_NOEXCEPT { 467 | // 'fmt::' is for bcc32. 468 | report_error(format_system_error, error_code, message); 469 | } 470 | 471 | #if FMT_USE_WINDOWS_H 472 | FMT_FUNC void report_windows_error( 473 | int error_code, fmt::StringRef message) FMT_NOEXCEPT { 474 | // 'fmt::' is for bcc32. 475 | report_error(internal::format_windows_error, error_code, message); 476 | } 477 | #endif 478 | 479 | FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) { 480 | MemoryWriter w; 481 | w.write(format_str, args); 482 | std::fwrite(w.data(), 1, w.size(), f); 483 | } 484 | 485 | FMT_FUNC void print(CStringRef format_str, ArgList args) { 486 | print(stdout, format_str, args); 487 | } 488 | 489 | FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) { 490 | char escape[] = "\x1b[30m"; 491 | escape[3] = static_cast('0' + c); 492 | std::fputs(escape, stdout); 493 | print(format, args); 494 | std::fputs(RESET_COLOR, stdout); 495 | } 496 | 497 | #ifndef FMT_HEADER_ONLY 498 | 499 | template struct internal::BasicData; 500 | 501 | // Explicit instantiations for char. 502 | 503 | template void internal::FixedBuffer::grow(std::size_t); 504 | 505 | template void internal::ArgMap::init(const ArgList &args); 506 | 507 | template FMT_API int internal::CharTraits::format_float( 508 | char *buffer, std::size_t size, const char *format, 509 | unsigned width, int precision, double value); 510 | 511 | template FMT_API int internal::CharTraits::format_float( 512 | char *buffer, std::size_t size, const char *format, 513 | unsigned width, int precision, long double value); 514 | 515 | // Explicit instantiations for wchar_t. 516 | 517 | template void internal::FixedBuffer::grow(std::size_t); 518 | 519 | template void internal::ArgMap::init(const ArgList &args); 520 | 521 | template FMT_API int internal::CharTraits::format_float( 522 | wchar_t *buffer, std::size_t size, const wchar_t *format, 523 | unsigned width, int precision, double value); 524 | 525 | template FMT_API int internal::CharTraits::format_float( 526 | wchar_t *buffer, std::size_t size, const wchar_t *format, 527 | unsigned width, int precision, long double value); 528 | 529 | #endif // FMT_HEADER_ONLY 530 | 531 | } // namespace fmt 532 | 533 | #ifdef _MSC_VER 534 | # pragma warning(pop) 535 | #endif 536 | -------------------------------------------------------------------------------- /ext/gsl/gsl: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #pragma once 18 | 19 | #ifndef GSL_GSL_H 20 | #define GSL_GSL_H 21 | 22 | #include // Ensures/Expects 23 | #include // finally()/narrow()/narrow_cast()... 24 | #include // multi_span, strided_span... 25 | #include // span 26 | #include // zstring, string_span, zstring_builder... 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #if defined(_MSC_VER) && _MSC_VER < 1910 33 | #pragma push_macro("constexpr") 34 | #define constexpr /*constexpr*/ 35 | 36 | #endif // defined(_MSC_VER) && _MSC_VER < 1910 37 | 38 | namespace gsl 39 | { 40 | 41 | // 42 | // GSL.owner: ownership pointers 43 | // 44 | using std::unique_ptr; 45 | using std::shared_ptr; 46 | 47 | // 48 | // owner 49 | // 50 | // owner is designed as a bridge for code that must deal directly with owning pointers for some reason 51 | // 52 | // T must be a pointer type 53 | // - disallow construction from any type other than pointer type 54 | // 55 | template ::value>> 56 | using owner = T; 57 | 58 | // 59 | // not_null 60 | // 61 | // Restricts a pointer or smart pointer to only hold non-null values. 62 | // 63 | // Has zero size overhead over T. 64 | // 65 | // If T is a pointer (i.e. T == U*) then 66 | // - allow construction from U* 67 | // - disallow construction from nullptr_t 68 | // - disallow default construction 69 | // - ensure construction from null U* fails 70 | // - allow implicit conversion to U* 71 | // 72 | template 73 | class not_null 74 | { 75 | public: 76 | static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); 77 | 78 | template ::value>> 79 | constexpr not_null(U&& u) : ptr_(std::forward(u)) 80 | { 81 | Expects(ptr_ != nullptr); 82 | } 83 | 84 | template ::value>> 85 | constexpr not_null(const not_null& other) : not_null(other.get()) 86 | { 87 | } 88 | 89 | not_null(const not_null& other) = default; 90 | not_null& operator=(const not_null& other) = default; 91 | 92 | constexpr T get() const 93 | { 94 | Ensures(ptr_ != nullptr); 95 | return ptr_; 96 | } 97 | 98 | constexpr operator T() const { return get(); } 99 | constexpr T operator->() const { return get(); } 100 | constexpr decltype(auto) operator*() const { return *get(); } 101 | 102 | // prevents compilation when someone attempts to assign a null pointer constant 103 | not_null(std::nullptr_t) = delete; 104 | not_null& operator=(std::nullptr_t) = delete; 105 | 106 | // unwanted operators...pointers only point to single objects! 107 | not_null& operator++() = delete; 108 | not_null& operator--() = delete; 109 | not_null operator++(int) = delete; 110 | not_null operator--(int) = delete; 111 | not_null& operator+=(std::ptrdiff_t) = delete; 112 | not_null& operator-=(std::ptrdiff_t) = delete; 113 | void operator[](std::ptrdiff_t) const = delete; 114 | 115 | private: 116 | T ptr_; 117 | }; 118 | 119 | template 120 | std::ostream& operator<<(std::ostream& os, const not_null& val) 121 | { 122 | os << val.get(); 123 | return os; 124 | } 125 | 126 | template 127 | auto operator==(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() == rhs.get()) 128 | { 129 | return lhs.get() == rhs.get(); 130 | } 131 | 132 | template 133 | auto operator!=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() != rhs.get()) 134 | { 135 | return lhs.get() != rhs.get(); 136 | } 137 | 138 | template 139 | auto operator<(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() < rhs.get()) 140 | { 141 | return lhs.get() < rhs.get(); 142 | } 143 | 144 | template 145 | auto operator<=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() <= rhs.get()) 146 | { 147 | return lhs.get() <= rhs.get(); 148 | } 149 | 150 | template 151 | auto operator>(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() > rhs.get()) 152 | { 153 | return lhs.get() > rhs.get(); 154 | } 155 | 156 | template 157 | auto operator>=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() >= rhs.get()) 158 | { 159 | return lhs.get() >= rhs.get(); 160 | } 161 | 162 | // more unwanted operators 163 | template 164 | std::ptrdiff_t operator-(const not_null&, const not_null&) = delete; 165 | template 166 | not_null operator-(const not_null&, std::ptrdiff_t) = delete; 167 | template 168 | not_null operator+(const not_null&, std::ptrdiff_t) = delete; 169 | template 170 | not_null operator+(std::ptrdiff_t, const not_null&) = delete; 171 | 172 | } // namespace gsl 173 | 174 | namespace std 175 | { 176 | template 177 | struct hash> 178 | { 179 | std::size_t operator()(const gsl::not_null& value) const { return hash{}(value); } 180 | }; 181 | 182 | } // namespace std 183 | 184 | #if defined(_MSC_VER) && _MSC_VER < 1910 185 | #undef constexpr 186 | #pragma pop_macro("constexpr") 187 | 188 | #endif // defined(_MSC_VER) && _MSC_VER < 1910 189 | 190 | #endif // GSL_GSL_H 191 | -------------------------------------------------------------------------------- /ext/gsl/gsl_algorithm: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #pragma once 18 | 19 | #ifndef GSL_ALGORITHM_H 20 | #define GSL_ALGORITHM_H 21 | 22 | #include 23 | 24 | #include 25 | 26 | #ifdef _MSC_VER 27 | #pragma warning(push) 28 | 29 | // turn off some warnings that are noisy about our Expects statements 30 | #pragma warning(disable : 4127) // conditional expression is constant 31 | #pragma warning(disable : 4996) // unsafe use of std::copy_n 32 | 33 | // blanket turn off warnings from CppCoreCheck for now 34 | // so people aren't annoyed by them when running the tool. 35 | // more targeted suppressions will be added in a future update to the GSL 36 | #pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) 37 | #endif // _MSC_VER 38 | 39 | namespace gsl 40 | { 41 | 42 | template 44 | void copy(span src, span dest) 45 | { 46 | static_assert(std::is_assignable::value, 47 | "Elements of source span can not be assigned to elements of destination span"); 48 | static_assert(SrcExtent == dynamic_extent || DestExtent == dynamic_extent || 49 | (SrcExtent <= DestExtent), 50 | "Source range is longer than target range"); 51 | 52 | Expects(dest.size() >= src.size()); 53 | std::copy_n(src.data(), src.size(), dest.data()); 54 | } 55 | 56 | } // namespace gsl 57 | 58 | #ifdef _MSC_VER 59 | #pragma warning(pop) 60 | #endif // _MSC_VER 61 | 62 | #endif // GSL_ALGORITHM_H 63 | -------------------------------------------------------------------------------- /ext/gsl/gsl_assert: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #pragma once 18 | 19 | #ifndef GSL_CONTRACTS_H 20 | #define GSL_CONTRACTS_H 21 | 22 | #include 23 | #include 24 | 25 | // 26 | // There are three configuration options for this GSL implementation's behavior 27 | // when pre/post conditions on the GSL types are violated: 28 | // 29 | // 1. GSL_TERMINATE_ON_CONTRACT_VIOLATION: std::terminate will be called (default) 30 | // 2. GSL_THROW_ON_CONTRACT_VIOLATION: a gsl::fail_fast exception will be thrown 31 | // 3. GSL_UNENFORCED_ON_CONTRACT_VIOLATION: nothing happens 32 | // 33 | #if !(defined(GSL_THROW_ON_CONTRACT_VIOLATION) || defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) || \ 34 | defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION)) 35 | #define GSL_TERMINATE_ON_CONTRACT_VIOLATION 36 | #endif 37 | 38 | #define GSL_STRINGIFY_DETAIL(x) #x 39 | #define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x) 40 | 41 | #if defined(__clang__) || defined(__GNUC__) 42 | #define GSL_LIKELY(x) __builtin_expect(!!(x), 1) 43 | #define GSL_UNLIKELY(x) __builtin_expect(!!(x), 0) 44 | #else 45 | #define GSL_LIKELY(x) (!!(x)) 46 | #define GSL_UNLIKELY(x) (!!(x)) 47 | #endif 48 | 49 | // 50 | // GSL_ASSUME(cond) 51 | // 52 | // Tell the optimizer that the predicate cond must hold. It is unspecified 53 | // whether or not cond is actually evaluated. 54 | // 55 | #ifdef _MSC_VER 56 | #define GSL_ASSUME(cond) __assume(cond) 57 | #elif defined(__clang__) 58 | #define GSL_ASSUME(cond) __builtin_assume(cond) 59 | #elif defined(__GNUC__) 60 | #define GSL_ASSUME(cond) ((cond) ? static_cast(0) : __builtin_unreachable()) 61 | #else 62 | #define GSL_ASSUME(cond) static_cast(!!(cond)) 63 | #endif 64 | 65 | // 66 | // GSL.assert: assertions 67 | // 68 | 69 | namespace gsl 70 | { 71 | struct fail_fast : public std::logic_error 72 | { 73 | explicit fail_fast(char const* const message) : std::logic_error(message) {} 74 | }; 75 | } 76 | 77 | #if defined(GSL_THROW_ON_CONTRACT_VIOLATION) 78 | 79 | #define GSL_CONTRACT_CHECK(type, cond) \ 80 | (GSL_LIKELY(cond) ? static_cast(0) \ 81 | : throw gsl::fail_fast("GSL: " type " failure at " __FILE__ \ 82 | ": " GSL_STRINGIFY(__LINE__))) 83 | 84 | #elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) 85 | 86 | #define GSL_CONTRACT_CHECK(type, cond) (GSL_LIKELY(cond) ? static_cast(0) : std::terminate()) 87 | 88 | #elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION) 89 | 90 | #define GSL_CONTRACT_CHECK(type, cond) GSL_ASSUME(cond) 91 | 92 | #endif 93 | 94 | #define Expects(cond) GSL_CONTRACT_CHECK("Precondition", cond) 95 | #define Ensures(cond) GSL_CONTRACT_CHECK("Postcondition", cond) 96 | 97 | #endif // GSL_CONTRACTS_H 98 | -------------------------------------------------------------------------------- /ext/gsl/gsl_byte: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #pragma once 18 | 19 | #ifndef GSL_BYTE_H 20 | #define GSL_BYTE_H 21 | 22 | #include 23 | 24 | #ifdef _MSC_VER 25 | 26 | #pragma warning(push) 27 | 28 | // don't warn about function style casts in byte related operators 29 | #pragma warning(disable : 26493) 30 | 31 | #ifndef GSL_USE_STD_BYTE 32 | // this tests if we are under MSVC and the standard lib has std::byte and it is enabled 33 | #if _MSC_VER >= 1911 && (!defined(_HAS_STD_BYTE) || _HAS_STD_BYTE) 34 | 35 | #define GSL_USE_STD_BYTE 1 36 | 37 | #else // _MSC_VER >= 1911 && (!defined(_HAS_STD_BYTE) || _HAS_STD_BYTE) 38 | 39 | #define GSL_USE_STD_BYTE 0 40 | 41 | #endif // _MSC_VER >= 1911 && (!defined(_HAS_STD_BYTE) || _HAS_STD_BYTE) 42 | #endif // GSL_USE_STD_BYTE 43 | 44 | #else // _MSC_VER 45 | 46 | #ifndef GSL_USE_STD_BYTE 47 | // this tests if we are under GCC or Clang with enough -std:c++1z power to get us std::byte 48 | #if defined(__cplusplus) && (__cplusplus >= 201703L) 49 | 50 | #define GSL_USE_STD_BYTE 1 51 | #include 52 | 53 | #else // defined(__cplusplus) && (__cplusplus >= 201703L) 54 | 55 | #define GSL_USE_STD_BYTE 0 56 | 57 | #endif //defined(__cplusplus) && (__cplusplus >= 201703L) 58 | #endif // GSL_USE_STD_BYTE 59 | 60 | #endif // _MSC_VER 61 | 62 | namespace gsl 63 | { 64 | #if GSL_USE_STD_BYTE 65 | 66 | 67 | using std::byte; 68 | using std::to_integer; 69 | 70 | #else // GSL_USE_STD_BYTE 71 | 72 | // This is a simple definition for now that allows 73 | // use of byte within span<> to be standards-compliant 74 | enum class byte : unsigned char 75 | { 76 | }; 77 | 78 | template ::value>> 79 | inline constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept 80 | { 81 | return b = byte(static_cast(b) << shift); 82 | } 83 | 84 | template ::value>> 85 | inline constexpr byte operator<<(byte b, IntegerType shift) noexcept 86 | { 87 | return byte(static_cast(b) << shift); 88 | } 89 | 90 | template ::value>> 91 | inline constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept 92 | { 93 | return b = byte(static_cast(b) >> shift); 94 | } 95 | 96 | template ::value>> 97 | inline constexpr byte operator>>(byte b, IntegerType shift) noexcept 98 | { 99 | return byte(static_cast(b) >> shift); 100 | } 101 | 102 | inline constexpr byte& operator|=(byte& l, byte r) noexcept 103 | { 104 | return l = byte(static_cast(l) | static_cast(r)); 105 | } 106 | 107 | inline constexpr byte operator|(byte l, byte r) noexcept 108 | { 109 | return byte(static_cast(l) | static_cast(r)); 110 | } 111 | 112 | inline constexpr byte& operator&=(byte& l, byte r) noexcept 113 | { 114 | return l = byte(static_cast(l) & static_cast(r)); 115 | } 116 | 117 | inline constexpr byte operator&(byte l, byte r) noexcept 118 | { 119 | return byte(static_cast(l) & static_cast(r)); 120 | } 121 | 122 | inline constexpr byte& operator^=(byte& l, byte r) noexcept 123 | { 124 | return l = byte(static_cast(l) ^ static_cast(r)); 125 | } 126 | 127 | inline constexpr byte operator^(byte l, byte r) noexcept 128 | { 129 | return byte(static_cast(l) ^ static_cast(r)); 130 | } 131 | 132 | inline constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } 133 | 134 | template ::value>> 135 | inline constexpr IntegerType to_integer(byte b) noexcept 136 | { 137 | return static_cast(b); 138 | } 139 | 140 | #endif // GSL_USE_STD_BYTE 141 | 142 | template 143 | inline constexpr byte to_byte_impl(T t) noexcept 144 | { 145 | static_assert( 146 | E, "gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. " 147 | "If you are calling to_byte with an integer contant use: gsl::to_byte() version."); 148 | return static_cast(t); 149 | } 150 | template <> 151 | inline constexpr byte to_byte_impl(unsigned char t) noexcept 152 | { 153 | return byte(t); 154 | } 155 | 156 | template 157 | inline constexpr byte to_byte(T t) noexcept 158 | { 159 | return to_byte_impl::value, T>(t); 160 | } 161 | 162 | template 163 | inline constexpr byte to_byte() noexcept 164 | { 165 | static_assert(I >= 0 && I <= 255, 166 | "gsl::byte only has 8 bits of storage, values must be in range 0-255"); 167 | return static_cast(I); 168 | } 169 | 170 | } // namespace gsl 171 | 172 | #ifdef _MSC_VER 173 | #pragma warning(pop) 174 | #endif // _MSC_VER 175 | 176 | #endif // GSL_BYTE_H 177 | -------------------------------------------------------------------------------- /ext/gsl/gsl_util: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #pragma once 18 | 19 | #ifndef GSL_UTIL_H 20 | #define GSL_UTIL_H 21 | 22 | #include // Ensures/Expects 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #if defined(_MSC_VER) 30 | 31 | #pragma warning(push) 32 | #pragma warning(disable : 4127) // conditional expression is constant 33 | 34 | #if _MSC_VER < 1910 35 | #pragma push_macro("constexpr") 36 | #define constexpr /*constexpr*/ 37 | #endif // _MSC_VER < 1910 38 | #endif // _MSC_VER 39 | 40 | namespace gsl 41 | { 42 | // 43 | // GSL.util: utilities 44 | // 45 | 46 | // final_act allows you to ensure something gets run at the end of a scope 47 | template 48 | class final_act 49 | { 50 | public: 51 | explicit final_act(F f) noexcept : f_(std::move(f)), invoke_(true) {} 52 | 53 | final_act(final_act&& other) noexcept : f_(std::move(other.f_)), invoke_(other.invoke_) 54 | { 55 | other.invoke_ = false; 56 | } 57 | 58 | final_act(const final_act&) = delete; 59 | final_act& operator=(const final_act&) = delete; 60 | 61 | ~final_act() noexcept 62 | { 63 | if (invoke_) f_(); 64 | } 65 | 66 | private: 67 | F f_; 68 | bool invoke_; 69 | }; 70 | 71 | // finally() - convenience function to generate a final_act 72 | template 73 | inline final_act finally(const F& f) noexcept 74 | { 75 | return final_act(f); 76 | } 77 | 78 | template 79 | inline final_act finally(F&& f) noexcept 80 | { 81 | return final_act(std::forward(f)); 82 | } 83 | 84 | // narrow_cast(): a searchable way to do narrowing casts of values 85 | template 86 | inline constexpr T narrow_cast(U&& u) noexcept 87 | { 88 | return static_cast(std::forward(u)); 89 | } 90 | 91 | struct narrowing_error : public std::exception 92 | { 93 | }; 94 | 95 | namespace details 96 | { 97 | template 98 | struct is_same_signedness 99 | : public std::integral_constant::value == std::is_signed::value> 100 | { 101 | }; 102 | } 103 | 104 | // narrow() : a checked version of narrow_cast() that throws if the cast changed the value 105 | template 106 | inline T narrow(U u) 107 | { 108 | T t = narrow_cast(u); 109 | if (static_cast(t) != u) throw narrowing_error(); 110 | if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) 111 | throw narrowing_error(); 112 | return t; 113 | } 114 | 115 | // 116 | // at() - Bounds-checked way of accessing builtin arrays, std::array, std::vector 117 | // 118 | template 119 | inline constexpr T& at(T (&arr)[N], const std::ptrdiff_t index) 120 | { 121 | Expects(index >= 0 && index < narrow_cast(N)); 122 | return arr[static_cast(index)]; 123 | } 124 | 125 | template 126 | inline constexpr auto at(Cont& cont, const std::ptrdiff_t index) -> decltype(cont[cont.size()]) 127 | { 128 | Expects(index >= 0 && index < narrow_cast(cont.size())); 129 | using size_type = decltype(cont.size()); 130 | return cont[static_cast(index)]; 131 | } 132 | 133 | template 134 | inline constexpr T at(const std::initializer_list cont, const std::ptrdiff_t index) 135 | { 136 | Expects(index >= 0 && index < narrow_cast(cont.size())); 137 | return *(cont.begin() + index); 138 | } 139 | 140 | } // namespace gsl 141 | 142 | #if defined(_MSC_VER) 143 | #if _MSC_VER < 1910 144 | #undef constexpr 145 | #pragma pop_macro("constexpr") 146 | 147 | #endif // _MSC_VER < 1910 148 | 149 | #pragma warning(pop) 150 | 151 | #endif // _MSC_VER 152 | 153 | #endif // GSL_UTIL_H 154 | -------------------------------------------------------------------------------- /ext/gsl/span: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4 | // 5 | // This code is licensed under the MIT License (MIT). 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13 | // THE SOFTWARE. 14 | // 15 | /////////////////////////////////////////////////////////////////////////////// 16 | 17 | #pragma once 18 | 19 | #ifndef GSL_SPAN_H 20 | #define GSL_SPAN_H 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef _MSC_VER 35 | #pragma warning(push) 36 | 37 | // turn off some warnings that are noisy about our Expects statements 38 | #pragma warning(disable : 4127) // conditional expression is constant 39 | #pragma warning(disable : 4702) // unreachable code 40 | 41 | // blanket turn off warnings from CppCoreCheck for now 42 | // so people aren't annoyed by them when running the tool. 43 | // more targeted suppressions will be added in a future update to the GSL 44 | #pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) 45 | 46 | #if _MSC_VER < 1910 47 | #pragma push_macro("constexpr") 48 | #define constexpr /*constexpr*/ 49 | 50 | #endif // _MSC_VER < 1910 51 | #endif // _MSC_VER 52 | 53 | #ifdef GSL_THROW_ON_CONTRACT_VIOLATION 54 | #define GSL_NOEXCEPT /*noexcept*/ 55 | #else 56 | #define GSL_NOEXCEPT noexcept 57 | #endif // GSL_THROW_ON_CONTRACT_VIOLATION 58 | 59 | namespace gsl 60 | { 61 | 62 | // [views.constants], constants 63 | constexpr const std::ptrdiff_t dynamic_extent = -1; 64 | 65 | template 66 | class span; 67 | 68 | // implementation details 69 | namespace details 70 | { 71 | template 72 | struct is_span_oracle : std::false_type 73 | { 74 | }; 75 | 76 | template 77 | struct is_span_oracle> : std::true_type 78 | { 79 | }; 80 | 81 | template 82 | struct is_span : public is_span_oracle> 83 | { 84 | }; 85 | 86 | template 87 | struct is_std_array_oracle : std::false_type 88 | { 89 | }; 90 | 91 | template 92 | struct is_std_array_oracle> : std::true_type 93 | { 94 | }; 95 | 96 | template 97 | struct is_std_array : public is_std_array_oracle> 98 | { 99 | }; 100 | 101 | template 102 | struct is_allowed_extent_conversion 103 | : public std::integral_constant 105 | { 106 | }; 107 | 108 | template 109 | struct is_allowed_element_type_conversion 110 | : public std::integral_constant::value> 111 | { 112 | }; 113 | 114 | template 115 | class span_iterator 116 | { 117 | using element_type_ = typename Span::element_type; 118 | 119 | public: 120 | using iterator_category = std::random_access_iterator_tag; 121 | using value_type = std::remove_cv_t; 122 | using difference_type = typename Span::index_type; 123 | 124 | using reference = std::conditional_t&; 125 | using pointer = std::add_pointer_t; 126 | 127 | span_iterator() = default; 128 | 129 | constexpr span_iterator(const Span* span, typename Span::index_type index) GSL_NOEXCEPT 130 | : span_(span), index_(index) 131 | { 132 | Expects(span == nullptr || (0 <= index_ && index <= span_->length())); 133 | } 134 | 135 | friend span_iterator; 136 | template* = nullptr> 137 | constexpr span_iterator(const span_iterator& other) GSL_NOEXCEPT 138 | : span_iterator(other.span_, other.index_) 139 | { 140 | } 141 | 142 | constexpr reference operator*() const GSL_NOEXCEPT 143 | { 144 | Expects(index_ != span_->length()); 145 | return *(span_->data() + index_); 146 | } 147 | 148 | constexpr pointer operator->() const GSL_NOEXCEPT 149 | { 150 | Expects(index_ != span_->length()); 151 | return span_->data() + index_; 152 | } 153 | 154 | constexpr span_iterator& operator++() GSL_NOEXCEPT 155 | { 156 | Expects(0 <= index_ && index_ != span_->length()); 157 | ++index_; 158 | return *this; 159 | } 160 | 161 | constexpr span_iterator operator++(int) GSL_NOEXCEPT 162 | { 163 | auto ret = *this; 164 | ++(*this); 165 | return ret; 166 | } 167 | 168 | constexpr span_iterator& operator--() GSL_NOEXCEPT 169 | { 170 | Expects(index_ != 0 && index_ <= span_->length()); 171 | --index_; 172 | return *this; 173 | } 174 | 175 | constexpr span_iterator operator--(int) GSL_NOEXCEPT 176 | { 177 | auto ret = *this; 178 | --(*this); 179 | return ret; 180 | } 181 | 182 | constexpr span_iterator operator+(difference_type n) const GSL_NOEXCEPT 183 | { 184 | auto ret = *this; 185 | return ret += n; 186 | } 187 | 188 | constexpr span_iterator& operator+=(difference_type n) GSL_NOEXCEPT 189 | { 190 | Expects((index_ + n) >= 0 && (index_ + n) <= span_->length()); 191 | index_ += n; 192 | return *this; 193 | } 194 | 195 | constexpr span_iterator operator-(difference_type n) const GSL_NOEXCEPT 196 | { 197 | auto ret = *this; 198 | return ret -= n; 199 | } 200 | 201 | constexpr span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } 202 | 203 | constexpr difference_type operator-(const span_iterator& rhs) const GSL_NOEXCEPT 204 | { 205 | Expects(span_ == rhs.span_); 206 | return index_ - rhs.index_; 207 | } 208 | 209 | constexpr reference operator[](difference_type n) const GSL_NOEXCEPT 210 | { 211 | return *(*this + n); 212 | } 213 | 214 | constexpr friend bool operator==(const span_iterator& lhs, 215 | const span_iterator& rhs) GSL_NOEXCEPT 216 | { 217 | return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_; 218 | } 219 | 220 | constexpr friend bool operator!=(const span_iterator& lhs, 221 | const span_iterator& rhs) GSL_NOEXCEPT 222 | { 223 | return !(lhs == rhs); 224 | } 225 | 226 | constexpr friend bool operator<(const span_iterator& lhs, 227 | const span_iterator& rhs) GSL_NOEXCEPT 228 | { 229 | Expects(lhs.span_ == rhs.span_); 230 | return lhs.index_ < rhs.index_; 231 | } 232 | 233 | constexpr friend bool operator<=(const span_iterator& lhs, 234 | const span_iterator& rhs) GSL_NOEXCEPT 235 | { 236 | return !(rhs < lhs); 237 | } 238 | 239 | constexpr friend bool operator>(const span_iterator& lhs, 240 | const span_iterator& rhs) GSL_NOEXCEPT 241 | { 242 | return rhs < lhs; 243 | } 244 | 245 | constexpr friend bool operator>=(const span_iterator& lhs, 246 | const span_iterator& rhs) GSL_NOEXCEPT 247 | { 248 | return !(rhs > lhs); 249 | } 250 | 251 | protected: 252 | const Span* span_ = nullptr; 253 | std::ptrdiff_t index_ = 0; 254 | }; 255 | 256 | template 257 | inline constexpr span_iterator 258 | operator+(typename span_iterator::difference_type n, 259 | const span_iterator& rhs) GSL_NOEXCEPT 260 | { 261 | return rhs + n; 262 | } 263 | 264 | template 265 | inline constexpr span_iterator 266 | operator-(typename span_iterator::difference_type n, 267 | const span_iterator& rhs) GSL_NOEXCEPT 268 | { 269 | return rhs - n; 270 | } 271 | 272 | template 273 | class extent_type 274 | { 275 | public: 276 | using index_type = std::ptrdiff_t; 277 | 278 | static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size."); 279 | 280 | constexpr extent_type() GSL_NOEXCEPT {} 281 | 282 | template 283 | constexpr extent_type(extent_type ext) 284 | { 285 | static_assert(Other == Ext || Other == dynamic_extent, 286 | "Mismatch between fixed-size extent and size of initializing data."); 287 | Expects(ext.size() == Ext); 288 | } 289 | 290 | constexpr extent_type(index_type size) { Expects(size == Ext); } 291 | 292 | constexpr index_type size() const GSL_NOEXCEPT { return Ext; } 293 | }; 294 | 295 | template <> 296 | class extent_type 297 | { 298 | public: 299 | using index_type = std::ptrdiff_t; 300 | 301 | template 302 | explicit constexpr extent_type(extent_type ext) : size_(ext.size()) 303 | { 304 | } 305 | 306 | explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } 307 | 308 | constexpr index_type size() const GSL_NOEXCEPT { return size_; } 309 | 310 | private: 311 | index_type size_; 312 | }; 313 | } // namespace details 314 | 315 | // [span], class template span 316 | template 317 | class span 318 | { 319 | public: 320 | // constants and types 321 | using element_type = ElementType; 322 | using value_type = std::remove_cv_t; 323 | using index_type = std::ptrdiff_t; 324 | using pointer = element_type*; 325 | using reference = element_type&; 326 | 327 | using iterator = details::span_iterator, false>; 328 | using const_iterator = details::span_iterator, true>; 329 | using reverse_iterator = std::reverse_iterator; 330 | using const_reverse_iterator = std::reverse_iterator; 331 | 332 | using size_type = index_type; 333 | 334 | constexpr static const index_type extent = Extent; 335 | 336 | // [span.cons], span constructors, copy, assignment, and destructor 337 | template " SFINAE, 339 | // since "std::enable_if_t" is ill-formed when Extent is greater than 0. 340 | class = std::enable_if_t<(Dependent || Extent <= 0)>> 341 | constexpr span() GSL_NOEXCEPT : storage_(nullptr, details::extent_type<0>()) 342 | { 343 | } 344 | 345 | constexpr span(std::nullptr_t) GSL_NOEXCEPT : span() {} 346 | 347 | constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} 348 | 349 | constexpr span(pointer firstElem, pointer lastElem) 350 | : storage_(firstElem, std::distance(firstElem, lastElem)) 351 | { 352 | } 353 | 354 | template 355 | constexpr span(element_type (&arr)[N]) GSL_NOEXCEPT 356 | : storage_(&arr[0], details::extent_type()) 357 | { 358 | } 359 | 360 | template > 361 | constexpr span(std::array& arr) GSL_NOEXCEPT 362 | : storage_(&arr[0], details::extent_type()) 363 | { 364 | } 365 | 366 | template 367 | constexpr span(const std::array, N>& arr) GSL_NOEXCEPT 368 | : storage_(&arr[0], details::extent_type()) 369 | { 370 | } 371 | 372 | template > 373 | constexpr span(const std::unique_ptr& ptr, index_type count) 374 | : storage_(ptr.get(), count) 375 | { 376 | } 377 | 378 | constexpr span(const std::unique_ptr& ptr) : storage_(ptr.get(), ptr.get() ? 1 : 0) 379 | { 380 | } 381 | constexpr span(const std::shared_ptr& ptr) : storage_(ptr.get(), ptr.get() ? 1 : 0) 382 | { 383 | } 384 | 385 | // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement 386 | // on Container to be a contiguous sequence container. 387 | template ::value && !details::is_std_array::value && 390 | std::is_convertible::value && 391 | std::is_convertible().data())>::value>> 393 | constexpr span(Container& cont) : span(cont.data(), narrow(cont.size())) 394 | { 395 | } 396 | 397 | template ::value && !details::is_span::value && 400 | std::is_convertible::value && 401 | std::is_convertible().data())>::value>> 403 | constexpr span(const Container& cont) : span(cont.data(), narrow(cont.size())) 404 | { 405 | } 406 | 407 | constexpr span(const span& other) GSL_NOEXCEPT = default; 408 | constexpr span(span&& other) GSL_NOEXCEPT = default; 409 | 410 | template < 411 | class OtherElementType, std::ptrdiff_t OtherExtent, 412 | class = std::enable_if_t< 413 | details::is_allowed_extent_conversion::value && 414 | details::is_allowed_element_type_conversion::value>> 415 | constexpr span(const span& other) 416 | : storage_(other.data(), details::extent_type(other.size())) 417 | { 418 | } 419 | 420 | template < 421 | class OtherElementType, std::ptrdiff_t OtherExtent, 422 | class = std::enable_if_t< 423 | details::is_allowed_extent_conversion::value && 424 | details::is_allowed_element_type_conversion::value>> 425 | constexpr span(span&& other) 426 | : storage_(other.data(), details::extent_type(other.size())) 427 | { 428 | } 429 | 430 | ~span() GSL_NOEXCEPT = default; 431 | constexpr span& operator=(const span& other) GSL_NOEXCEPT = default; 432 | 433 | constexpr span& operator=(span&& other) GSL_NOEXCEPT = default; 434 | 435 | // [span.sub], span subviews 436 | template 437 | constexpr span first() const 438 | { 439 | Expects(Count >= 0 && Count <= size()); 440 | return {data(), Count}; 441 | } 442 | 443 | template 444 | constexpr span last() const 445 | { 446 | Expects(Count >= 0 && Count <= size()); 447 | return {data() + (size() - Count), Count}; 448 | } 449 | 450 | template 451 | constexpr span subspan() const 452 | { 453 | Expects((Offset == 0 || (Offset > 0 && Offset <= size())) && 454 | (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); 455 | return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; 456 | } 457 | 458 | constexpr span first(index_type count) const 459 | { 460 | Expects(count >= 0 && count <= size()); 461 | return {data(), count}; 462 | } 463 | 464 | constexpr span last(index_type count) const 465 | { 466 | Expects(count >= 0 && count <= size()); 467 | return {data() + (size() - count), count}; 468 | } 469 | 470 | constexpr span subspan(index_type offset, 471 | index_type count = dynamic_extent) const 472 | { 473 | Expects((offset == 0 || (offset > 0 && offset <= size())) && 474 | (count == dynamic_extent || (count >= 0 && offset + count <= size()))); 475 | return {data() + offset, count == dynamic_extent ? size() - offset : count}; 476 | } 477 | 478 | // [span.obs], span observers 479 | constexpr index_type length() const GSL_NOEXCEPT { return size(); } 480 | constexpr index_type size() const GSL_NOEXCEPT { return storage_.size(); } 481 | constexpr index_type length_bytes() const GSL_NOEXCEPT { return size_bytes(); } 482 | constexpr index_type size_bytes() const GSL_NOEXCEPT 483 | { 484 | return size() * narrow_cast(sizeof(element_type)); 485 | } 486 | constexpr bool empty() const GSL_NOEXCEPT { return size() == 0; } 487 | 488 | // [span.elem], span element access 489 | constexpr reference operator[](index_type idx) const 490 | { 491 | Expects(idx >= 0 && idx < storage_.size()); 492 | return data()[idx]; 493 | } 494 | 495 | constexpr reference at(index_type idx) const { return this->operator[](idx); } 496 | constexpr reference operator()(index_type idx) const { return this->operator[](idx); } 497 | constexpr pointer data() const GSL_NOEXCEPT { return storage_.data(); } 498 | 499 | // [span.iter], span iterator support 500 | iterator begin() const GSL_NOEXCEPT { return {this, 0}; } 501 | iterator end() const GSL_NOEXCEPT { return {this, length()}; } 502 | 503 | const_iterator cbegin() const GSL_NOEXCEPT { return {this, 0}; } 504 | const_iterator cend() const GSL_NOEXCEPT { return {this, length()}; } 505 | 506 | reverse_iterator rbegin() const GSL_NOEXCEPT { return reverse_iterator{end()}; } 507 | reverse_iterator rend() const GSL_NOEXCEPT { return reverse_iterator{begin()}; } 508 | 509 | const_reverse_iterator crbegin() const GSL_NOEXCEPT { return const_reverse_iterator{cend()}; } 510 | const_reverse_iterator crend() const GSL_NOEXCEPT { return const_reverse_iterator{cbegin()}; } 511 | 512 | private: 513 | // this implementation detail class lets us take advantage of the 514 | // empty base class optimization to pay for only storage of a single 515 | // pointer in the case of fixed-size spans 516 | template 517 | class storage_type : public ExtentType 518 | { 519 | public: 520 | template 521 | constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) 522 | { 523 | Expects((!data && ExtentType::size() == 0) || (data && ExtentType::size() >= 0)); 524 | } 525 | 526 | constexpr pointer data() const GSL_NOEXCEPT { return data_; } 527 | 528 | private: 529 | pointer data_; 530 | }; 531 | 532 | storage_type> storage_; 533 | }; 534 | 535 | // [span.comparison], span comparison operators 536 | template 537 | inline constexpr bool operator==(const span& l, 538 | const span& r) 539 | { 540 | return std::equal(l.begin(), l.end(), r.begin(), r.end()); 541 | } 542 | 543 | template 544 | inline constexpr bool operator!=(const span& l, 545 | const span& r) 546 | { 547 | return !(l == r); 548 | } 549 | 550 | template 551 | inline constexpr bool operator<(const span& l, 552 | const span& r) 553 | { 554 | return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); 555 | } 556 | 557 | template 558 | inline constexpr bool operator<=(const span& l, 559 | const span& r) 560 | { 561 | return !(l > r); 562 | } 563 | 564 | template 565 | inline constexpr bool operator>(const span& l, 566 | const span& r) 567 | { 568 | return r < l; 569 | } 570 | 571 | template 572 | inline constexpr bool operator>=(const span& l, 573 | const span& r) 574 | { 575 | return !(l < r); 576 | } 577 | 578 | namespace details 579 | { 580 | // if we only supported compilers with good constexpr support then 581 | // this pair of classes could collapse down to a constexpr function 582 | 583 | // we should use a narrow_cast<> to go to std::size_t, but older compilers may not see it as 584 | // constexpr 585 | // and so will fail compilation of the template 586 | template 587 | struct calculate_byte_size 588 | : std::integral_constant(sizeof(ElementType) * 590 | static_cast(Extent))> 591 | { 592 | }; 593 | 594 | template 595 | struct calculate_byte_size 596 | : std::integral_constant 597 | { 598 | }; 599 | } 600 | 601 | // [span.objectrep], views of object representation 602 | template 603 | span::value> 604 | as_bytes(span s) GSL_NOEXCEPT 605 | { 606 | return {reinterpret_cast(s.data()), s.size_bytes()}; 607 | } 608 | 609 | template ::value>> 611 | span::value> 612 | as_writeable_bytes(span s) GSL_NOEXCEPT 613 | { 614 | return {reinterpret_cast(s.data()), s.size_bytes()}; 615 | } 616 | 617 | // 618 | // make_span() - Utility functions for creating spans 619 | // 620 | template 621 | span make_span(ElementType* ptr, typename span::index_type count) 622 | { 623 | return span(ptr, count); 624 | } 625 | 626 | template 627 | span make_span(ElementType* firstElem, ElementType* lastElem) 628 | { 629 | return span(firstElem, lastElem); 630 | } 631 | 632 | template 633 | span make_span(ElementType (&arr)[N]) 634 | { 635 | return span(arr); 636 | } 637 | 638 | template 639 | span make_span(Container& cont) 640 | { 641 | return span(cont); 642 | } 643 | 644 | template 645 | span make_span(const Container& cont) 646 | { 647 | return span(cont); 648 | } 649 | 650 | template 651 | span make_span(Ptr& cont, std::ptrdiff_t count) 652 | { 653 | return span(cont, count); 654 | } 655 | 656 | template 657 | span make_span(Ptr& cont) 658 | { 659 | return span(cont); 660 | } 661 | 662 | // Specialization of gsl::at for span 663 | template 664 | inline constexpr ElementType& at(const span& s, std::ptrdiff_t index) 665 | { 666 | // No bounds checking here because it is done in span::operator[] called below 667 | return s[index]; 668 | } 669 | 670 | } // namespace gsl 671 | 672 | #undef GSL_NOEXCEPT 673 | 674 | #ifdef _MSC_VER 675 | #if _MSC_VER < 1910 676 | #undef constexpr 677 | #pragma pop_macro("constexpr") 678 | 679 | #endif // _MSC_VER < 1910 680 | 681 | #pragma warning(pop) 682 | #endif // _MSC_VER 683 | 684 | #endif // GSL_SPAN_H 685 | -------------------------------------------------------------------------------- /src/file/dbcfile.h: -------------------------------------------------------------------------------- 1 | #ifndef DBC_FILE_H 2 | #define DBC_FILE_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | namespace dbc { 14 | 15 | 16 | using Value_map = std::map; 17 | 18 | 19 | enum class Byte_order { Moto, Intel }; 20 | enum class Value_sign { Signed, Unsigned }; 21 | 22 | 23 | struct Signal_meta_data 24 | { 25 | std::int8_t factor_precision = 7; 26 | std::int8_t offset_precision = 7; 27 | std::int8_t minimum_precision = 7; 28 | std::int8_t maximum_precision = 7; 29 | }; 30 | 31 | 32 | struct Value_def 33 | { 34 | std::uint32_t frame_id; 35 | std::string signal_name; 36 | std::map value_definitions; 37 | }; 38 | 39 | 40 | struct Signal_def 41 | { 42 | std::string name; 43 | std::int32_t multiplex_value; 44 | bool multiplex_switch; 45 | std::uint32_t pos; 46 | std::uint32_t len; 47 | Byte_order order; 48 | Value_sign sign; 49 | double factor; 50 | double offset; 51 | double minimum; 52 | double maximum; 53 | std::string unit; 54 | std::vector receiver; 55 | Value_map value_definitions; 56 | Signal_meta_data meta_data; 57 | }; 58 | 59 | 60 | struct Frame_def 61 | { 62 | std::uint32_t id; 63 | std::string name; 64 | std::uint32_t dlc; 65 | std::string transmitter; 66 | bool multiplexer; 67 | bool multiplexer_extended; 68 | std::vector signal_defs; 69 | }; 70 | 71 | 72 | struct File 73 | { 74 | std::string name; 75 | std::vector nodes; 76 | std::vector frame_defs; 77 | }; 78 | 79 | 80 | inline const Frame_def* find_frame_def(const File& f, std::uint32_t id) 81 | { 82 | auto it = std::find_if(std::begin(f.frame_defs), std::end(f.frame_defs), 83 | [id](const auto& m){ return m.id == id; }); 84 | if (it != std::end(f.frame_defs)) 85 | return &*it; 86 | return nullptr; 87 | } 88 | 89 | 90 | inline const Frame_def* find_frame_def(const File& f, std::string_view name) 91 | { 92 | auto it = std::find_if(std::begin(f.frame_defs), std::end(f.frame_defs), 93 | [name](const auto& m){ return m.name == name; }); 94 | if (it != std::end(f.frame_defs)) 95 | return &*it; 96 | return nullptr; 97 | } 98 | 99 | 100 | inline const Signal_def* find_signal_def(const Frame_def& f, std::string_view name) 101 | { 102 | auto it = std::find_if(std::begin(f.signal_defs), std::end(f.signal_defs), 103 | [name](const auto& s){ return s.name == name; }); 104 | if (it != std::end(f.signal_defs)) 105 | return &*it; 106 | return nullptr; 107 | } 108 | 109 | 110 | } // namespace dbc 111 | 112 | 113 | #endif // DBC_FILE_H 114 | -------------------------------------------------------------------------------- /src/file/dbcmetaparser.cpp: -------------------------------------------------------------------------------- 1 | #include "dbcmetaparser.h" 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | 11 | namespace xpr = boost::xpressive; 12 | 13 | 14 | namespace { 15 | 16 | 17 | using namespace xpr; 18 | mark_tag factor{1}, offset{2}, minimum{3}, maximum{4}, fractional{1}, exponent{2}; 19 | 20 | // Fields in DBC signal definition using double type 21 | sregex decimal_values = '(' >> (factor = -+_) >> ',' >> (offset = -+_) >> ')' >> blank 22 | >> '[' >> (minimum = -+_) >> '|' >> (maximum = -+_) >> ']'; 23 | 24 | // Decimal number with optional fractional part and scientific notation 25 | sregex decimal = +_d >> !('.' >> (fractional = +_d)) >> !((set='e','E') 26 | >> (exponent = !(set='-','+') >> +_d)); 27 | 28 | 29 | } // namespace 30 | 31 | 32 | dbc::Signal_meta_data dbc::meta::parse_signal(const std::string& line) 33 | { 34 | // Calculate required decimal places for correct reproduction (not significant digits) 35 | auto fractional_precision = [](auto&& match) -> std::int8_t { 36 | xpr::smatch number; 37 | if (xpr::regex_match(match.first, match.second, number, decimal)) { 38 | auto exp = number[exponent].length() > 0 ? std::stoi(number[exponent].str()) : 0; 39 | auto prec = number[fractional].length() - exp; 40 | return prec < 0 ? 0 : prec; // Positive exponent may lead to negative precision, e.g 1.0e9 41 | } 42 | return 0; 43 | }; 44 | 45 | xpr::smatch values; 46 | if (xpr::regex_search(line, values, decimal_values)) { 47 | try { 48 | Signal_meta_data meta_data; 49 | meta_data.factor_precision = fractional_precision(values[factor]); 50 | meta_data.offset_precision = fractional_precision(values[offset]); 51 | meta_data.minimum_precision = fractional_precision(values[minimum]); 52 | meta_data.maximum_precision = fractional_precision(values[maximum]); 53 | return meta_data; 54 | } 55 | catch (const std::logic_error& e) { 56 | return {}; 57 | } 58 | } 59 | 60 | return {}; 61 | } 62 | -------------------------------------------------------------------------------- /src/file/dbcmetaparser.h: -------------------------------------------------------------------------------- 1 | #ifndef DBC_METAPARSER_H 2 | #define DBC_METAPARSER_H 3 | 4 | 5 | // Parse additional data, e.g. required decimal precision of factor and offset 6 | 7 | 8 | #include 9 | #include "file/dbcfile.h" 10 | 11 | 12 | namespace dbc::meta { 13 | 14 | 15 | Signal_meta_data parse_signal(const std::string& line); 16 | 17 | 18 | } // namespace dbc::meta 19 | 20 | 21 | #endif // DBC_METAPARSER_H 22 | -------------------------------------------------------------------------------- /src/file/dbcparser.cpp: -------------------------------------------------------------------------------- 1 | #include "dbcparser.h" 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "file/dbcmetaparser.h" 17 | 18 | 19 | BOOST_FUSION_ADAPT_STRUCT( 20 | dbc::Value_def, 21 | (std::uint32_t, frame_id) 22 | (std::string, signal_name) 23 | (dbc::Value_map, value_definitions) 24 | ) 25 | 26 | 27 | BOOST_FUSION_ADAPT_STRUCT( 28 | dbc::Signal_def, 29 | (std::string, name) 30 | (std::int32_t, multiplex_value) 31 | (bool, multiplex_switch) 32 | (std::uint32_t, pos) 33 | (std::uint32_t, len) 34 | (dbc::Byte_order, order) 35 | (dbc::Value_sign, sign) 36 | (double, factor) 37 | (double, offset) 38 | (double, minimum) 39 | (double, maximum) 40 | (std::string, unit) 41 | (std::vector, receiver) 42 | (dbc::Value_map, value_definitions) 43 | (dbc::Signal_meta_data, meta_data) 44 | ) 45 | 46 | 47 | BOOST_FUSION_ADAPT_STRUCT( 48 | dbc::Frame_def, 49 | (std::uint32_t, id) 50 | (std::string, name) 51 | (std::uint32_t, dlc) 52 | (std::string, transmitter) 53 | (bool, multiplexer) 54 | (bool, multiplexer_extended) 55 | (std::vector, signal_defs) 56 | ) 57 | 58 | 59 | namespace fsys = std::experimental::filesystem; 60 | namespace x3 = boost::spirit::x3; 61 | namespace ascii = boost::spirit::x3::ascii; 62 | namespace latin1 = boost::spirit::x3::iso8859_1; 63 | 64 | 65 | namespace { 66 | 67 | 68 | inline dbc::Frame_def* get_def(std::vector& frame_defs, std::uint32_t id) 69 | { 70 | auto it = std::find_if(std::begin(frame_defs), std::end(frame_defs), 71 | [id](const auto& m){ return m.id == id; }); 72 | if (it != std::end(frame_defs)) 73 | return &*it; 74 | return nullptr; 75 | } 76 | 77 | 78 | inline dbc::Signal_def* get_def(dbc::Frame_def* f, std::string_view signal_name) 79 | { 80 | if (!f) 81 | return nullptr; 82 | auto it = std::find_if(std::begin(f->signal_defs), std::end(f->signal_defs), 83 | [signal_name](const auto& s){ return s.name == signal_name; }); 84 | if (it != std::end(f->signal_defs)) 85 | return &*it; 86 | return nullptr; 87 | } 88 | 89 | 90 | inline dbc::Signal_def* get_def(std::vector& frame_defs, std::uint32_t frame_id, 91 | std::string_view signal_name) 92 | { 93 | return get_def(get_def(frame_defs, frame_id), signal_name); 94 | } 95 | 96 | 97 | struct signs_ : x3::symbols 98 | { 99 | signs_() 100 | { 101 | add 102 | ("+", dbc::Value_sign::Unsigned) 103 | ("-", dbc::Value_sign::Signed) 104 | ; 105 | } 106 | } signs; 107 | 108 | 109 | struct oders_ : x3::symbols 110 | { 111 | oders_() 112 | { 113 | add 114 | ("0", dbc::Byte_order::Moto) 115 | ("1", dbc::Byte_order::Intel) 116 | ; 117 | } 118 | } orders; 119 | 120 | 121 | } // namspace 122 | 123 | 124 | namespace parsers { 125 | 126 | 127 | using x3::char_; 128 | using x3::long_; 129 | using x3::ulong_; 130 | using x3::double_; 131 | using x3::lit; 132 | using x3::attr; 133 | using x3::lexeme; 134 | using x3::omit; 135 | 136 | 137 | // Black magic 138 | template static auto as = [](auto p){ return x3::rule{"as"} = p; }; 139 | 140 | 141 | const auto identifier = +char_("a-zA-Z0-9_"); 142 | const auto multiplex_indicator = (('m' >> long_) | attr(-1)) >> (('M' >> attr(true)) | attr(false)); 143 | const auto signal_name = lexeme[identifier]; 144 | const auto quoted_string = lexeme['"' >> *(latin1::char_ - '"') >> '"']; 145 | const auto value_pair = as>(long_ >> omit[+ascii::space] 146 | >> quoted_string); 147 | 148 | 149 | x3::rule const signal = "signal"; 150 | const auto signal_def = 151 | lit("SG_") 152 | >> signal_name >> multiplex_indicator >> ':' 153 | >> ulong_ >> '|' >> ulong_ >> '@' 154 | >> orders >> signs 155 | >> '(' >> double_ >> ',' >> double_ >> ')' 156 | >> '[' >> double_ >> '|' >> double_ >> ']' 157 | >> quoted_string 158 | >> identifier % ',' 159 | >> attr(dbc::Value_map{}) 160 | >> attr(dbc::Signal_meta_data{}) 161 | ; 162 | BOOST_SPIRIT_DEFINE(signal) 163 | 164 | 165 | x3::rule const frame = "frame"; 166 | const auto frame_def = lit("BO_") >> ulong_ >> identifier >> ':' >> ulong_ >> identifier 167 | >> attr(false) >> attr(false) >> attr(std::vector{}); 168 | BOOST_SPIRIT_DEFINE(frame) 169 | 170 | 171 | x3::rule const value_def = "value_def"; 172 | const auto value_def_def = lit("VAL_") >> ulong_ >> signal_name 173 | >> lexeme[value_pair % omit[+ascii::space]]; 174 | BOOST_SPIRIT_DEFINE(value_def) 175 | 176 | 177 | } // namespace parsers 178 | 179 | 180 | namespace { 181 | 182 | 183 | inline void parse_nodes(std::string_view line, std::vector& nodes) 184 | { 185 | x3::phrase_parse(std::begin(line), std::end(line), 186 | x3::lit("BU_:") >> parsers::identifier % ',', ascii::space, nodes); 187 | } 188 | 189 | 190 | std::tuple parse_value_def(std::string_view line) 191 | { 192 | dbc::Value_def value_def; 193 | bool success = x3::phrase_parse(std::begin(line), std::end(line), parsers::value_def_def, 194 | ascii::space, value_def); 195 | return {success, value_def}; 196 | } 197 | 198 | 199 | std::tuple parse_signal_def(std::string_view line) 200 | { 201 | dbc::Signal_def signal; 202 | bool success = x3::phrase_parse(std::begin(line), std::end(line), parsers::signal, ascii::space, 203 | signal); 204 | return {success, signal}; 205 | } 206 | 207 | 208 | std::tuple parse_frame_def(std::string_view line) 209 | { 210 | dbc::Frame_def message; 211 | bool success = x3::phrase_parse(std::begin(line), std::end(line), parsers::frame, ascii::space, 212 | message); 213 | return {success, message}; 214 | } 215 | 216 | 217 | [[maybe_unused]] inline bool messages_block_done(std::string_view line) 218 | { 219 | // We're not interested in comments, etc. 220 | using latin1::space; 221 | using x3::lit; 222 | return x3::parse(std::begin(line), std::end(line), *space 223 | >> (lit("CM_") | lit("BA_DEF_") | lit("BA_")) >> space); 224 | } 225 | 226 | 227 | [[maybe_unused]] void sort_signals(dbc::Frame_def& frame_def) 228 | { 229 | std::sort(std::begin(frame_def.signal_defs), std::end(frame_def.signal_defs), 230 | [](const auto& a, const auto& b){ return a.pos < b.pos; }); 231 | } 232 | 233 | 234 | void mark_multiplexer(std::vector& frame_defs) 235 | { 236 | // Frames with multiple switch signals or multiplexed switch signals are extended multiplexer 237 | for (auto& fd : frame_defs) { 238 | int switch_signals = 0; 239 | for (const auto& sd : fd.signal_defs) { 240 | if (sd.multiplex_switch) 241 | switch_signals++; 242 | if (!fd.multiplexer_extended && sd.multiplex_switch && sd.multiplex_value > -1) 243 | fd.multiplexer_extended = true; 244 | } 245 | fd.multiplexer = switch_signals > 0; 246 | if (!fd.multiplexer_extended) 247 | fd.multiplexer_extended = switch_signals > 1; 248 | } 249 | } 250 | 251 | 252 | } // namespace 253 | 254 | 255 | dbc::File dbc::parse(std::string_view filepath) 256 | { 257 | File dbc_file; 258 | std::vector value_definitions; 259 | 260 | std::ifstream fs{std::string{filepath}}; 261 | if (!fs.is_open()) 262 | throw Parse_error{"Could not open file"}; 263 | 264 | dbc_file.name = fsys::path{filepath}.filename().string(); 265 | 266 | std::string line; 267 | while (std::getline(fs, line)) { 268 | if (dbc_file.nodes.empty()) 269 | parse_nodes(line, dbc_file.nodes); 270 | 271 | if (auto [success, signal_def] = parse_signal_def(line); success) { 272 | if (dbc_file.frame_defs.empty()) { 273 | throw Parse_error{"Signal defined before frame"}; 274 | } 275 | else { 276 | dbc_file.frame_defs.back().signal_defs.push_back(signal_def); 277 | dbc_file.frame_defs.back().signal_defs.back().meta_data = dbc::meta::parse_signal(line); 278 | } 279 | } 280 | else if (auto [success, frame_def] = parse_frame_def(line); success) { 281 | dbc_file.frame_defs.push_back(frame_def); 282 | } 283 | else if (auto [success, value_def] = parse_value_def(line); success) { 284 | value_definitions.push_back(std::move(value_def)); 285 | } 286 | } 287 | 288 | for (auto& v : value_definitions) { 289 | if (auto* signal_def = get_def(dbc_file.frame_defs, v.frame_id, v.signal_name); signal_def) 290 | signal_def->value_definitions = std::move(v.value_definitions); 291 | } 292 | 293 | mark_multiplexer(dbc_file.frame_defs); 294 | for (auto& fd : dbc_file.frame_defs) { 295 | if (fd.multiplexer_extended) 296 | throw Parse_error{"Extended multiplexing not supported"}; 297 | } 298 | 299 | return dbc_file; 300 | } 301 | -------------------------------------------------------------------------------- /src/file/dbcparser.h: -------------------------------------------------------------------------------- 1 | #ifndef DBC_PARSER_H 2 | #define DBC_PARSER_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "file/dbcfile.h" 10 | 11 | 12 | namespace dbc { 13 | 14 | 15 | class Parse_error : public std::runtime_error 16 | { 17 | public: 18 | explicit Parse_error(const std::string& s) : std::runtime_error{s} {} 19 | explicit Parse_error(const char* s) : std::runtime_error{s} {} 20 | }; 21 | 22 | 23 | File parse(std::string_view filepath); 24 | 25 | 26 | } // namespace dbc 27 | 28 | 29 | #endif // DBC_PARSER_H 30 | -------------------------------------------------------------------------------- /src/file/dbcwriter.cpp: -------------------------------------------------------------------------------- 1 | #include "dbcwriter.h" 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "fmt/fmtlib.h" 9 | 10 | 11 | using namespace fmt::literals; 12 | 13 | 14 | namespace { 15 | 16 | 17 | inline void write_header(std::ofstream& fs) 18 | { 19 | // Empty version and required sections for normal DBC 20 | fs << "VERSION \"\"\n\nNS_:\n\nBS_:\n\n"; 21 | } 22 | 23 | 24 | inline void write_nodes(std::ofstream& fs, const std::vector& nodes) 25 | { 26 | fs << "BU_:"; 27 | if (!nodes.empty()) { 28 | fs << " "; 29 | auto last = std::end(nodes) - 1; 30 | for (auto it = std::begin(nodes); it != last; ++it) 31 | fs << *it << ','; 32 | fs << *last; 33 | } 34 | fs << "\n\n"; 35 | } 36 | 37 | 38 | inline void write_frame_def(std::ofstream& fs, const dbc::Frame_def& fd) 39 | { 40 | // BO_ id name: dlc transmitter 41 | fs << fmt::format(R"(BO_ {} {}: {} {})", fd.id, fd.name, fd.dlc, 42 | fd.transmitter.empty() ? "Vector__XXX" : fd.transmitter) << '\n'; 43 | } 44 | 45 | 46 | inline void write_signal_def(std::ofstream& fs, const dbc::Signal_def& sd) 47 | { 48 | auto to_string = [](auto&& receiver) { 49 | std::string s{receiver.front()}; 50 | for (auto it = std::begin(receiver) + 1; it != std::end(receiver); ++it) { 51 | s += ','; 52 | s += *it; 53 | } 54 | return s; 55 | }; 56 | 57 | // SG_ name : pos|len@order_and_type (factor,offset) [min|max] "unit" receiver{, receiver} 58 | fs << fmt::format(R"( SG_ {} {}: {}|{}@{}{} ({:.{}f},{:.{}f}) [{:.{}f}|{:.{}f}] "{}" {})", 59 | sd.name, 60 | sd.multiplex_switch ? "M " : sd.multiplex_value >= 0 ? "m{} "_format(sd.multiplex_value) : "", 61 | sd.pos, 62 | sd.len, 63 | static_cast(sd.order), 64 | sd.sign == dbc::Value_sign::Signed ? "-" : "+", 65 | sd.factor, 66 | sd.meta_data.factor_precision, 67 | sd.offset, 68 | sd.meta_data.offset_precision, 69 | sd.minimum, 70 | sd.meta_data.minimum_precision, 71 | sd.maximum, 72 | sd.meta_data.maximum_precision, 73 | sd.unit, 74 | sd.receiver.empty() ? "Vector__XXX" : to_string(sd.receiver) 75 | ) << '\n'; 76 | } 77 | 78 | 79 | } // namespace 80 | 81 | 82 | void dbc::write(const File& dbc_file, std::string_view filepath) 83 | { 84 | std::ofstream fs{std::string{filepath}}; 85 | if (!fs.is_open()) 86 | throw Write_error{"Could not open file"}; 87 | 88 | write_header(fs); 89 | write_nodes(fs, dbc_file.nodes); 90 | 91 | for (const auto& frame_def : dbc_file.frame_defs) { 92 | write_frame_def(fs, frame_def); 93 | for (const auto& signal_def : frame_def.signal_defs) { 94 | write_signal_def(fs, signal_def); 95 | } 96 | fs << '\n'; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/file/dbcwriter.h: -------------------------------------------------------------------------------- 1 | #ifndef DBC_WRITER_H 2 | #define DBC_WRITER_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "file/dbcfile.h" 10 | 11 | 12 | namespace dbc { 13 | 14 | 15 | class Write_error : public std::runtime_error 16 | { 17 | public: 18 | explicit Write_error(const std::string& s) : std::runtime_error{s} {} 19 | explicit Write_error(const char* s) : std::runtime_error{s} {} 20 | }; 21 | 22 | 23 | void write(const File& dbc_file, std::string_view filepath); 24 | 25 | 26 | } // namespace dbc 27 | 28 | 29 | #endif // DBC_WRITER_H 30 | -------------------------------------------------------------------------------- /src/file/jsonreader.cpp: -------------------------------------------------------------------------------- 1 | #include "jsonreader.h" 2 | 3 | 4 | #include 5 | #include 6 | #include "json.hpp" 7 | 8 | #include "tincan/errors.h" 9 | #include "tincan/canframedef.h" 10 | #include "tincan/cansignaldef.h" 11 | 12 | 13 | namespace fs = std::experimental::filesystem; 14 | using json = nlohmann::json; 15 | 16 | 17 | tin::Can_bus_def tin::read_json(std::string_view filepath) 18 | { 19 | tin::Can_bus_def bus_def; 20 | 21 | std::ifstream fs{std::string{filepath}}; 22 | if (!fs.is_open()) 23 | throw tin::File_error{"Could not open file"}; 24 | 25 | std::stringstream ss; 26 | ss << fs.rdbuf(); 27 | fs.close(); 28 | auto j = json::parse(ss); 29 | if (j.empty()) 30 | throw tin::File_error{"File is empty"}; 31 | 32 | bus_def.source_name = fs::path{filepath}.filename().string(); 33 | 34 | for (const auto& f : j["frames"]) { 35 | bus_def.frame_defs.emplace_back(); 36 | auto& frame_def = bus_def.frame_defs.back(); 37 | frame_def.id = f["id"]; 38 | frame_def.dlc = f["dlc"]; 39 | frame_def.name = f["name"]; 40 | 41 | for (const auto& s : f["signals"]) { 42 | frame_def.can_signal_defs.emplace_back(); 43 | auto& signal_def = frame_def.can_signal_defs.back(); 44 | signal_def.order = static_cast(s["byte_order"]); 45 | signal_def.sign = static_cast(s["value_sign"]); 46 | signal_def.pos = s["pos"]; 47 | signal_def.len = s["len"]; 48 | signal_def.multiplex_switch = s["multiplex_switch"]; 49 | signal_def.multiplex_value = s["multiplex_value"]; 50 | signal_def.factor = s["factor"]; 51 | signal_def.offset = s["offset"]; 52 | signal_def.minimum = s["minimum"]; 53 | signal_def.maximum = s["maximum"]; 54 | //signal_def.unit = s["unit"]; // TODO: UTF-8 error 55 | signal_def.name = s["name"]; 56 | } 57 | } 58 | 59 | return bus_def; 60 | } 61 | -------------------------------------------------------------------------------- /src/file/jsonreader.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_JSONREADER_H 2 | #define TIN_JSONREADER_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "tincan/canbusdef.h" 9 | 10 | 11 | namespace tin { 12 | 13 | 14 | tin::Can_bus_def read_json(std::string_view filepath); 15 | 16 | 17 | } // namespace tin 18 | 19 | 20 | #endif // TIN_JSONREADER_H 21 | -------------------------------------------------------------------------------- /src/file/jsonwriter.cpp: -------------------------------------------------------------------------------- 1 | #include "jsonwriter.h" 2 | 3 | 4 | #include 5 | #include "json.hpp" 6 | 7 | #include "tincan/errors.h" 8 | #include "tincan/canframedef.h" 9 | #include "tincan/cansignaldef.h" 10 | 11 | 12 | using json = nlohmann::json; 13 | 14 | 15 | void tin::write_json(const tin::Can_bus_def& bus_def, std::string_view filepath) 16 | { 17 | json j; 18 | j["version"] = "0.1"; 19 | j["format"] = "CAN"; 20 | auto frames = json::array(); 21 | 22 | for (const auto& frame_def : bus_def.frame_defs) { 23 | json frame; 24 | frame["id"] = frame_def.id; 25 | frame["dlc"] = frame_def.dlc; 26 | frame["name"] = frame_def.name; 27 | auto can_signals = json::array(); 28 | 29 | for (const auto& signal_def : frame_def.can_signal_defs) { 30 | json signal; 31 | signal["byte_order"] = static_cast(signal_def.order); 32 | signal["value_sign"] = static_cast(signal_def.sign); 33 | signal["pos"] = signal_def.pos; 34 | signal["len"] = signal_def.len; 35 | signal["multiplex_switch"] = signal_def.multiplex_switch; 36 | signal["multiplex_value"] = signal_def.multiplex_value; 37 | signal["factor"] = signal_def.factor; 38 | signal["offset"] = signal_def.offset; 39 | signal["minimum"] = signal_def.minimum; 40 | signal["maximum"] = signal_def.maximum; 41 | //signal["unit"] = signal_def.unit; // TODO: UTF-8 error 42 | signal["name"] = signal_def.name; 43 | can_signals.push_back(signal); 44 | } 45 | 46 | frame["signals"] = can_signals; 47 | frames.push_back(frame); 48 | } 49 | 50 | j["frames"] = frames; 51 | 52 | std::ofstream fs{std::string{filepath}}; 53 | if (!fs.is_open()) 54 | throw tin::File_error{"Could not open file"}; 55 | 56 | fs << j.dump(2); 57 | } 58 | -------------------------------------------------------------------------------- /src/file/jsonwriter.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_JSONWRITER_H 2 | #define TIN_JSONWRITER_H 3 | 4 | 5 | #include 6 | #include "tincan/canbusdef.h" 7 | 8 | 9 | namespace tin { 10 | 11 | 12 | void write_json(const tin::Can_bus_def& bus_def, std::string_view filename); 13 | 14 | 15 | } // namespace tin 16 | 17 | 18 | #endif // TIN_JSONWRITER_H 19 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ui/mainwindow.h" 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | QApplication a(argc, argv); 10 | a.setStyle(QStyleFactory::create("Fusion")); 11 | 12 | QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); 13 | 14 | QPalette dark; 15 | dark.setColor(QPalette::Window, QColor(53, 53, 53)); 16 | dark.setColor(QPalette::WindowText, Qt::white); 17 | dark.setColor(QPalette::Base, QColor(25, 25, 25)); 18 | dark.setColor(QPalette::AlternateBase, QColor(53, 53, 53)); 19 | dark.setColor(QPalette::ToolTipBase, Qt::white); 20 | dark.setColor(QPalette::ToolTipText, Qt::white); 21 | dark.setColor(QPalette::Text, Qt::white); 22 | dark.setColor(QPalette::Button, QColor(53, 53, 53)); 23 | dark.setColor(QPalette::ButtonText, Qt::white); 24 | dark.setColor(QPalette::BrightText, Qt::red); 25 | dark.setColor(QPalette::Link, QColor(42, 130, 218)); 26 | dark.setColor(QPalette::Highlight, QColor(51,231,247)); 27 | dark.setColor(QPalette::HighlightedText, Qt::black); 28 | a.setPalette(dark); 29 | 30 | Main_window w; 31 | w.show(); 32 | 33 | return a.exec(); 34 | } 35 | -------------------------------------------------------------------------------- /src/models/canbusdefmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "canbusdefmodel.h" 2 | 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "tincan/cansignaldef.h" 11 | #include "tincan/canframedef.h" 12 | #include "tincan/canbusdef.h" 13 | #include "models/treeitemid.h" 14 | #include "models/treeitem.h" 15 | #include "models/cansignaldefitem.h" 16 | #include "models/canframedefitem.h" 17 | 18 | 19 | tin::Can_bus_def_model::Can_bus_def_model(QObject* parent) : Tree_model{parent} 20 | { 21 | construct(); 22 | } 23 | 24 | 25 | void tin::Can_bus_def_model::construct() 26 | { 27 | root_item_ = std::make_unique(Item_id::Root); 28 | column_headers_ = {"Object", "ID / Pos", "Len", "Mux", "Value", "Order", "Factor", "Offset", 29 | "Min", "Max", "Unit"}; 30 | column_widths_ = { 200, 80, 40, 40, 70, 60, 140, 140, 140, 140, 80 }; 31 | } 32 | 33 | 34 | QVariant tin::Can_bus_def_model::data(const QModelIndex& index, int role) const 35 | { 36 | if (!index.isValid()) 37 | return QVariant{}; 38 | 39 | switch (role) { 40 | case Qt::SizeHintRole: return QSize{column_widths_[static_cast(index.column())], row_height_}; 41 | case Qt::ForegroundRole: { 42 | auto* item = static_cast(index.internalPointer()); 43 | auto id = item->id(); 44 | if (id == Item_id::Can_frame_def) 45 | return QBrush{QColor{"#33E7F7"}}; 46 | return Tree_model::data(index, role); 47 | } 48 | } 49 | 50 | return Tree_model::data(index, role); 51 | } 52 | 53 | 54 | void tin::Can_bus_def_model::reset() 55 | { 56 | beginResetModel(); 57 | root_item_ = std::make_unique(Item_id::Root); 58 | endResetModel(); 59 | } 60 | 61 | 62 | void tin::Can_bus_def_model::set(const Can_bus_def* bus_def) 63 | { 64 | if (!bus_def) 65 | return; 66 | 67 | beginResetModel(); 68 | root_item_ = std::make_unique(Item_id::Root); 69 | for (const auto& frame_def : bus_def->frame_defs) { 70 | auto frame_def_item = std::make_unique(&frame_def, root_item_.get()); 71 | for (const auto& signal_def : frame_def.can_signal_defs) { 72 | frame_def_item->add_child(std::make_unique(&signal_def, 73 | frame_def_item.get())); 74 | } 75 | root_item_->add_child(std::move(frame_def_item)); 76 | } 77 | endResetModel(); 78 | } 79 | -------------------------------------------------------------------------------- /src/models/canbusdefmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANBUSDEFMODEL_H 2 | #define TIN_CANBUSDEFMODEL_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "models/treemodel.h" 13 | 14 | 15 | namespace tin { 16 | 17 | 18 | class Can_bus_def; 19 | 20 | 21 | class Can_bus_def_model final : public Tree_model 22 | { 23 | Q_OBJECT 24 | 25 | public: 26 | explicit Can_bus_def_model(QObject* parent = nullptr); 27 | virtual void construct() override; 28 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; 29 | void set(const Can_bus_def* bus_def); 30 | void set_row_height(int h) { row_height_ = h; } 31 | 32 | public slots: 33 | void reset(); 34 | 35 | private: 36 | std::vector column_widths_; 37 | int row_height_ = 20; 38 | }; 39 | 40 | 41 | } // namespace tin 42 | 43 | 44 | #endif // TIN_CANBUSDEFMODEL_H 45 | -------------------------------------------------------------------------------- /src/models/canbusmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "canbusmodel.h" 2 | 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "tincan/cansignal.h" 11 | #include "tincan/canframe.h" 12 | #include "tincan/canbus.h" 13 | #include "models/treeitemid.h" 14 | #include "models/treeitem.h" 15 | #include "models/cansignalitem.h" 16 | #include "models/canframeitem.h" 17 | 18 | 19 | tin::Can_bus_model::Can_bus_model(const Can_bus* can_bus, QObject* parent) 20 | : Tree_model{parent}, can_bus_{can_bus} 21 | { 22 | construct(); 23 | 24 | // Deferred update of tree items since multiple frames with low cycle times cause a CPU usage 25 | // spike due to an excessive amount of data update calls. Be aware, depening on the refresh 26 | // rate and a frame's cycle time this may lead to some changes not being displayed! 27 | auto* update_timer = new QTimer{this}; 28 | connect(update_timer, &QTimer::timeout, this, [this]{ 29 | for (auto id : deferred_updates_) 30 | update_data(id); 31 | deferred_updates_.clear(); 32 | }); 33 | update_timer->start(53); 34 | } 35 | 36 | 37 | void tin::Can_bus_model::construct() 38 | { 39 | root_item_ = std::make_unique(Item_id::Root); 40 | column_headers_ = {"Object", "ID / Phys", "Time / Unit", "Cycle", "Length", "Data"}; 41 | } 42 | 43 | 44 | QVariant tin::Can_bus_model::data(const QModelIndex& index, int role) const 45 | { 46 | if (!index.isValid()) 47 | return QVariant{}; 48 | 49 | switch (role) { 50 | case Qt::ForegroundRole: { 51 | auto* item = reinterpret_cast(index.internalPointer()); 52 | if (item->id() == Item_id::Can_frame) { 53 | const auto* frame_item = reinterpret_cast(item); 54 | if (!frame_item->frame()->alive) 55 | return QBrush{QColor{"#787878"}}; 56 | if (item->child_count() > 0) 57 | return QBrush{QColor{"#33E7F7"}}; 58 | } 59 | return Tree_model::data(index, role); 60 | } 61 | } 62 | 63 | return Tree_model::data(index, role); 64 | } 65 | 66 | 67 | void tin::Can_bus_model::reset() 68 | { 69 | beginResetModel(); 70 | frame_items_.clear(); 71 | root_item_ = std::make_unique(Item_id::Root); 72 | endResetModel(); 73 | } 74 | 75 | 76 | void tin::Can_bus_model::update_data_deferred(std::uint32_t frame_id) 77 | { 78 | deferred_updates_.insert(frame_id); 79 | } 80 | 81 | 82 | void tin::Can_bus_model::update_data(std::uint32_t frame_id) 83 | { 84 | if (auto* item = find_item(frame_id); item) { // Update existing frame 85 | auto row = item->row(); 86 | if (row >= 0) 87 | dataChanged(index(row, 0), index(row, columnCount()), QVector{Qt::DisplayRole}); 88 | } 89 | else { // Add new frame 90 | if (auto* frame = can_bus_->frame(frame_id); frame) { 91 | auto frame_item = std::make_unique(frame, root_item_.get()); 92 | for (const auto& signal : frame->can_signals) 93 | frame_item->add_child(std::make_unique(&signal, frame_item.get())); 94 | frame_items_.insert({frame_id, frame_item.get()}); 95 | beginInsertRows(QModelIndex{}, rowCount(), rowCount()); 96 | root_item_->add_child(std::move(frame_item)); 97 | endInsertRows(); 98 | } 99 | } 100 | } 101 | 102 | 103 | tin::Can_frame_item* tin::Can_bus_model::find_item(std::uint32_t frame_id) 104 | { 105 | auto it = frame_items_.find(frame_id); 106 | if (it != std::end(frame_items_)) 107 | return it->second; 108 | return nullptr; 109 | } 110 | -------------------------------------------------------------------------------- /src/models/canbusmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANBUSMODEL_H 2 | #define TIN_CANBUSMODEL_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "models/canframeitem.h" 13 | #include "models/treemodel.h" 14 | 15 | 16 | namespace tin { 17 | 18 | 19 | class Can_bus; 20 | 21 | 22 | class Can_bus_model final : public Tree_model 23 | { 24 | Q_OBJECT 25 | 26 | public: 27 | explicit Can_bus_model(const Can_bus* can_bus, QObject* parent = nullptr); 28 | virtual void construct() override; 29 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; 30 | 31 | public slots: 32 | void reset(); 33 | void update_data_deferred(std::uint32_t frame_id); 34 | void update_data(std::uint32_t frame_id); 35 | 36 | private: 37 | Can_frame_item* find_item(std::uint32_t frame_id); 38 | 39 | const Can_bus* can_bus_; 40 | std::unordered_map frame_items_; 41 | std::unordered_set deferred_updates_; 42 | }; 43 | 44 | 45 | } // namespace tin 46 | 47 | 48 | #endif // TIN_CANBUSMODEL_H 49 | -------------------------------------------------------------------------------- /src/models/canframedefitem.cpp: -------------------------------------------------------------------------------- 1 | #include "canframedefitem.h" 2 | 3 | 4 | #include 5 | 6 | 7 | QVariant tin::Can_frame_def_item::data(int column) const 8 | { 9 | switch (column) { 10 | case 0: return QString::fromStdString(can_frame_def_->name); 11 | case 1: return QString{"0x"} + QString::number(can_frame_def_->id, 16).toUpper(); 12 | case 2: return can_frame_def_->dlc; 13 | } 14 | 15 | return QVariant{}; 16 | } 17 | -------------------------------------------------------------------------------- /src/models/canframedefitem.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANFRAMEDEFITEM_H 2 | #define TIN_CANFRAMEDEFITEM_H 3 | 4 | 5 | #include 6 | #include "tincan/canframedef.h" 7 | #include "models/treeitem.h" 8 | #include "models/treeitemid.h" 9 | 10 | 11 | namespace tin { 12 | 13 | 14 | class Can_frame_def_item final : public Tree_item 15 | { 16 | public: 17 | Can_frame_def_item(const Can_frame_def* can_frame_def, Tree_item* parent = nullptr) 18 | : Tree_item{Item_id::Can_frame_def, parent}, can_frame_def_{can_frame_def} {} 19 | virtual QVariant data(int column) const override; 20 | 21 | private: 22 | const Can_frame_def* can_frame_def_; 23 | }; 24 | 25 | 26 | } // namespace tin 27 | 28 | 29 | #endif // TIN_CANFRAMEDEFITEM_H 30 | -------------------------------------------------------------------------------- /src/models/canframeitem.cpp: -------------------------------------------------------------------------------- 1 | #include "canframeitem.h" 2 | 3 | 4 | #include 5 | 6 | #include "util.h" 7 | #include "tincan/canframe.h" 8 | 9 | 10 | QVariant tin::Can_frame_item::data(int column) const 11 | { 12 | switch (column) { 13 | case 0: return can_frame_->frame_def ? QString::fromStdString(can_frame_->frame_def->name) : "FRAME"; 14 | case 1: return QString{"0x"} + QString::number(can_frame_->id, 16).toUpper(); 15 | case 2: return can_frame_->receive_time; 16 | case 3: return QString::number(can_frame_->mean_cycle_time / 1000.0, 'f', 3); 17 | case 4: return can_frame_->length; 18 | case 5: { 19 | QString hex_data; 20 | for (int i=can_frame_->length-1; i>=0 && i<8; --i) { 21 | hex_data += QString::number(can_frame_->raw_data[i], 16).rightJustified(2, '0'); 22 | hex_data += " "; 23 | } 24 | return hex_data.toUpper(); 25 | } 26 | } 27 | 28 | return QVariant{}; 29 | } 30 | -------------------------------------------------------------------------------- /src/models/canframeitem.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANFRAMEITEM_H 2 | #define TIN_CANFRAMEITEM_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "models/treeitem.h" 11 | #include "models/treeitemid.h" 12 | 13 | 14 | namespace tin { 15 | 16 | 17 | struct Can_frame; 18 | 19 | 20 | class Can_frame_item final : public Tree_item 21 | { 22 | public: 23 | Can_frame_item(const Can_frame* can_frame, Tree_item* parent = nullptr) 24 | : Tree_item{Item_id::Can_frame, parent}, can_frame_{can_frame} {} 25 | const Can_frame* frame() const { return can_frame_; } 26 | virtual QVariant data(int column) const override; 27 | 28 | private: 29 | const Can_frame* can_frame_; 30 | }; 31 | 32 | 33 | } // namespace tin 34 | 35 | 36 | #endif // TIN_CANFRAMEITEM_H 37 | -------------------------------------------------------------------------------- /src/models/cansignaldefitem.cpp: -------------------------------------------------------------------------------- 1 | #include "cansignaldefitem.h" 2 | 3 | 4 | #include 5 | #include "tincan/cansignaldef.h" 6 | 7 | 8 | QVariant tin::Can_signal_def_item::data(int column) const 9 | { 10 | const auto* s = can_signal_def_; 11 | switch (column) { 12 | case 0: return QString::fromStdString(s->name); 13 | case 1: return s->pos; 14 | case 2: return s->len; 15 | case 3: { 16 | if (s->multiplex_switch) 17 | return "S"; 18 | else if (s->multiplex_value != -1) 19 | return s->multiplex_value; 20 | return ""; 21 | } 22 | case 4: return s->sign == tin::Value_sign::Unsigned ? "unsigned" : "signed"; 23 | case 5: return s->order == tin::Byte_order::Intel ? "intel" : "moto"; 24 | case 6: return QString::number(s->factor, 'f', s->meta_data.factor_precision); 25 | case 7: return QString::number(s->offset, 'f', s->meta_data.offset_precision); 26 | case 8: return QString::number(s->minimum, 'f', s->meta_data.minimum_precision); 27 | case 9: return QString::number(s->maximum, 'f', s->meta_data.maximum_precision); 28 | case 10: return QString::fromLocal8Bit(s->unit.c_str()); 29 | } 30 | 31 | return QVariant{}; 32 | } 33 | -------------------------------------------------------------------------------- /src/models/cansignaldefitem.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANSIGNALDEFITEM_H 2 | #define TIN_CANSIGNALDEFITEM_H 3 | 4 | 5 | #include 6 | #include "models/treeitem.h" 7 | #include "models/treeitemid.h" 8 | 9 | 10 | namespace tin { 11 | 12 | 13 | struct Can_signal_def; 14 | 15 | 16 | class Can_signal_def_item final : public Tree_item 17 | { 18 | public: 19 | Can_signal_def_item(const Can_signal_def* can_signal_def, Tree_item* parent = nullptr) 20 | : Tree_item{Item_id::Can_signal_def, parent}, can_signal_def_{can_signal_def} {} 21 | const Can_signal_def* signal_def() const { return can_signal_def_; } 22 | virtual QVariant data(int column) const override; 23 | 24 | private: 25 | const Can_signal_def* can_signal_def_; 26 | }; 27 | 28 | 29 | } // namespace tin 30 | 31 | 32 | #endif // TIN_CANSIGNALDEFITEM_H 33 | -------------------------------------------------------------------------------- /src/models/cansignalitem.cpp: -------------------------------------------------------------------------------- 1 | #include "cansignalitem.h" 2 | 3 | 4 | #include 5 | #include 6 | #include "tincan/cansignal.h" 7 | 8 | 9 | QVariant tin::Can_signal_item::data(int column) const 10 | { 11 | switch (column) { 12 | case 0: return QString::fromStdString(can_signal_->signal_def->name); 13 | case 1: { 14 | if (!can_signal_->signal_def->value_definitions.empty()) { 15 | std::int32_t index = 0; 16 | // Slice and dice 17 | if (std::holds_alternative(can_signal_->raw)) 18 | index = static_cast(std::get(can_signal_->raw)); 19 | else 20 | index = static_cast(std::get(can_signal_->raw)); 21 | const auto& value_defs = can_signal_->signal_def->value_definitions; 22 | if (auto it = value_defs.find(index); it != std::end(value_defs)) 23 | return QString::fromStdString(it->second); 24 | } 25 | 26 | if (std::holds_alternative(can_signal_->phys)) { 27 | return std::get(can_signal_->phys); 28 | } 29 | else if (std::holds_alternative(can_signal_->phys)) { 30 | return std::get(can_signal_->phys); 31 | } 32 | else { 33 | return QString::number(std::get(can_signal_->phys), 'f', 34 | can_signal_->signal_def->meta_data.factor_precision); 35 | } 36 | } 37 | case 2: return QString::fromStdString(can_signal_->signal_def->unit); 38 | case 5: 39 | if (std::holds_alternative(can_signal_->raw)) 40 | return std::get(can_signal_->raw); 41 | return std::get(can_signal_->raw); 42 | } 43 | 44 | return QVariant{}; 45 | } 46 | -------------------------------------------------------------------------------- /src/models/cansignalitem.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANSIGNALITEM_H 2 | #define TIN_CANSIGNALITEM_H 3 | 4 | 5 | #include 6 | #include "models/treeitem.h" 7 | #include "models/treeitemid.h" 8 | 9 | 10 | namespace tin { 11 | 12 | 13 | struct Can_signal; 14 | 15 | 16 | class Can_signal_item final : public Tree_item 17 | { 18 | public: 19 | Can_signal_item(const Can_signal* can_signal, Tree_item* parent = nullptr) 20 | : Tree_item{Item_id::Can_signal, parent}, can_signal_{can_signal} {} 21 | const Can_signal* signal() const { return can_signal_; } 22 | virtual QVariant data(int column) const override; 23 | 24 | private: 25 | const Can_signal* can_signal_; 26 | }; 27 | 28 | 29 | } // namespace tin 30 | 31 | 32 | #endif // TIN_CANSIGNALITEM_H 33 | -------------------------------------------------------------------------------- /src/models/treeitem.cpp: -------------------------------------------------------------------------------- 1 | #include "treeitem.h" 2 | 3 | 4 | #include 5 | 6 | 7 | int tin::Tree_item::row() const 8 | { 9 | if (parent_) { 10 | return parent_->row(this); 11 | } 12 | 13 | return -1; 14 | } 15 | 16 | 17 | int tin::Tree_item::row(const Tree_item* item) const 18 | { 19 | auto it = std::find_if(std::begin(children_), std::end(children_), 20 | [item](const auto& c) { return c.get() == item; }); 21 | if (it != std::end(children_)) 22 | return std::distance(std::begin(children_), it); 23 | 24 | return -1; 25 | } 26 | 27 | 28 | tin::Tree_item* tin::Tree_item::child(int row) const 29 | { 30 | return row >= 0 && row < static_cast(children_.size()) ? children_[row].get() : nullptr; 31 | } 32 | -------------------------------------------------------------------------------- /src/models/treeitem.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_TREEITEM_H 2 | #define TIN_TREEITEM_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "models/treeitemid.h" 11 | 12 | 13 | namespace tin { 14 | 15 | 16 | class Tree_item 17 | { 18 | public: 19 | Tree_item(Item_id id = Item_id::Unspecified, Tree_item* parent = nullptr) 20 | : id_{id}, parent_{parent} {} 21 | virtual ~Tree_item() {} 22 | 23 | Item_id id() const { return id_; } 24 | 25 | int row() const; 26 | int row(const Tree_item* item) const; 27 | 28 | int child_count() const { return static_cast(children_.size()); } 29 | 30 | Tree_item* parent() const { return parent_; } 31 | Tree_item* child(int row) const; 32 | 33 | void add_child(std::unique_ptr&& item) { children_.push_back(std::move(item)); } 34 | 35 | virtual QVariant data([[maybe_unused]] int column) const { return QVariant{}; } 36 | 37 | protected: 38 | Item_id id_; 39 | Tree_item* parent_; 40 | std::vector> children_; 41 | }; 42 | 43 | 44 | } // namespace tin 45 | 46 | 47 | #endif // TIN_TREEITEM_H 48 | -------------------------------------------------------------------------------- /src/models/treeitemid.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_TREEITEMID_H 2 | #define TIN_TREEITEMID_H 3 | 4 | 5 | namespace tin { 6 | 7 | 8 | enum class Item_id { 9 | Unspecified, 10 | Root, 11 | Can_frame, 12 | Can_signal, 13 | Can_frame_def, 14 | Can_signal_def, 15 | Value_def 16 | }; 17 | 18 | 19 | } // namespace tin 20 | 21 | 22 | #endif // TIN_TREEITEMID_H 23 | -------------------------------------------------------------------------------- /src/models/treemodel.cpp: -------------------------------------------------------------------------------- 1 | #include "treemodel.h" 2 | 3 | 4 | QVariant tin::Tree_model::headerData(int section, Qt::Orientation orientation, int role) const 5 | { 6 | if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { 7 | return column_headers_[section]; 8 | } 9 | 10 | return QVariant{}; 11 | } 12 | 13 | 14 | QModelIndex tin::Tree_model::index(int row, int column, const QModelIndex& parent) const 15 | { 16 | if (!hasIndex(row, column, parent)) 17 | return QModelIndex(); 18 | 19 | Tree_item* item; 20 | if (!parent.isValid()) 21 | item = root_item_.get(); 22 | else 23 | item = static_cast(parent.internalPointer()); 24 | 25 | item = item->child(row); 26 | if (item) 27 | return createIndex(row, column, item); 28 | 29 | return QModelIndex{}; 30 | } 31 | 32 | 33 | QModelIndex tin::Tree_model::parent(const QModelIndex& index) const 34 | { 35 | if (!index.isValid()) 36 | return QModelIndex{}; 37 | 38 | auto* item = static_cast(index.internalPointer())->parent(); 39 | 40 | if (item == root_item_.get() || item == nullptr) 41 | return QModelIndex{}; 42 | 43 | return createIndex(item->row(), 0, item); 44 | } 45 | 46 | 47 | int tin::Tree_model::rowCount(const QModelIndex& parent) const 48 | { 49 | if (parent.column() > 0) 50 | return 0; 51 | 52 | Tree_item* item; 53 | if (!parent.isValid()) 54 | item = root_item_.get(); 55 | else 56 | item = static_cast(parent.internalPointer()); 57 | 58 | return item->child_count(); 59 | } 60 | 61 | 62 | int tin::Tree_model::columnCount(const QModelIndex& parent) const 63 | { 64 | Q_UNUSED(parent) 65 | return static_cast(column_headers_.size()); 66 | } 67 | 68 | 69 | QVariant tin::Tree_model::data(const QModelIndex& index, int role) const 70 | { 71 | if (!index.isValid()) 72 | return QVariant{}; 73 | if (role != Qt::DisplayRole) 74 | return QVariant{}; 75 | 76 | auto* item = static_cast(index.internalPointer()); 77 | return item->data(index.column()); 78 | } 79 | -------------------------------------------------------------------------------- /src/models/treemodel.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_TREEMODEL_H 2 | #define TIN_TREEMODEL_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "models/treeitem.h" 13 | 14 | 15 | namespace tin { 16 | 17 | 18 | class Tree_model : public QAbstractItemModel 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit Tree_model(QObject* parent = nullptr) : QAbstractItemModel{parent} {} 24 | virtual ~Tree_model() {} 25 | virtual void construct() = 0; 26 | 27 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; 28 | QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex{}) const override; 29 | QModelIndex parent(const QModelIndex& index) const override; 30 | 31 | int rowCount(const QModelIndex& parent = QModelIndex{}) const override; 32 | int columnCount(const QModelIndex& parent = QModelIndex{}) const override; 33 | 34 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; 35 | 36 | protected: 37 | std::unique_ptr root_item_; 38 | std::vector column_headers_; 39 | }; 40 | 41 | 42 | } // namespace tin 43 | 44 | 45 | #endif // TIN_TREEMODEL_H 46 | -------------------------------------------------------------------------------- /src/models/valuedefitem.cpp: -------------------------------------------------------------------------------- 1 | #include "valuedefitem.h" 2 | 3 | 4 | 5 | QVariant tin::Value_def_item::data(int column) const 6 | { 7 | switch (column) { 8 | case 0: return value_; 9 | case 1: return definition_; 10 | } 11 | 12 | return QVariant{}; 13 | } 14 | -------------------------------------------------------------------------------- /src/models/valuedefitem.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_VALUEDEFITEM_H 2 | #define TIN_VALUEDEFITEM_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "models/treeitem.h" 12 | #include "models/treeitemid.h" 13 | 14 | 15 | namespace tin { 16 | 17 | 18 | class Value_def_item final : public Tree_item 19 | { 20 | public: 21 | Value_def_item(std::int32_t value, const std::string& definition, Tree_item* parent = nullptr) 22 | : Tree_item{Item_id::Value_def, parent}, value_{value}, 23 | definition_{QString::fromStdString(definition)} {} 24 | virtual QVariant data(int column) const override; 25 | 26 | private: 27 | std::int32_t value_; 28 | QString definition_; 29 | }; 30 | 31 | 32 | } // namespace tin 33 | 34 | 35 | #endif // TIN_VALUEDEFITEM_H 36 | -------------------------------------------------------------------------------- /src/models/valuedefmodel.cpp: -------------------------------------------------------------------------------- 1 | #include "valuedefmodel.h" 2 | 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "models/treeitemid.h" 9 | #include "models/treeitem.h" 10 | #include "models/valuedefitem.h" 11 | 12 | 13 | tin::Value_def_model::Value_def_model(QObject* parent) : Tree_model{parent} 14 | { 15 | construct(); 16 | } 17 | 18 | 19 | void tin::Value_def_model::construct() 20 | { 21 | root_item_ = std::make_unique(Item_id::Root); 22 | column_headers_ = {"Value", "Definition"}; 23 | column_widths_ = { 200, 200 }; 24 | } 25 | 26 | 27 | QVariant tin::Value_def_model::data(const QModelIndex& index, int role) const 28 | { 29 | if (!index.isValid()) 30 | return QVariant{}; 31 | 32 | switch (role) { 33 | case Qt::SizeHintRole: 34 | return QSize{column_widths_[static_cast(index.column())], row_height_}; 35 | } 36 | 37 | return Tree_model::data(index, role); 38 | } 39 | 40 | 41 | void tin::Value_def_model::reset() 42 | { 43 | beginResetModel(); 44 | root_item_ = std::make_unique(Item_id::Root); 45 | endResetModel(); 46 | } 47 | 48 | 49 | void tin::Value_def_model::set(const std::map& value_definitions) 50 | { 51 | beginResetModel(); 52 | root_item_ = std::make_unique(Item_id::Root); 53 | for (const auto& [key, value] : value_definitions) { 54 | auto valdef_item = std::make_unique(key, value, root_item_.get()); 55 | root_item_->add_child(std::move(valdef_item)); 56 | } 57 | endResetModel(); 58 | } 59 | -------------------------------------------------------------------------------- /src/models/valuedefmodel.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_VALUEDEFMODEL_H 2 | #define TIN_VALUEDEFMODEL_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include "models/treemodel.h" 13 | 14 | 15 | namespace tin { 16 | 17 | 18 | class Value_def_model final : public Tree_model 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit Value_def_model(QObject* parent = nullptr); 24 | virtual void construct() override; 25 | QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; 26 | void set(const std::map& value_definitions); 27 | void set_row_height(int h) { row_height_ = h; } 28 | 29 | public slots: 30 | void reset(); 31 | 32 | private: 33 | std::vector column_widths_; 34 | int row_height_ = 20; 35 | }; 36 | 37 | 38 | } // namespace tin 39 | 40 | 41 | #endif // TIN_VALUEDEFMODEL_H 42 | -------------------------------------------------------------------------------- /src/network/canudpreceiver.cpp: -------------------------------------------------------------------------------- 1 | #include "canudpreceiver.h" 2 | 3 | 4 | #include "util.h" 5 | 6 | 7 | void network::Can_udp_receiver::handle_receive(gsl::span buffer) 8 | { 9 | if (buffer.size() == sizeof(std::uint64_t) + sizeof(tin::Can_raw_frame)) { 10 | // Received CAN frame with a timestamp from the CAN receiver 11 | auto* time = reinterpret_cast(buffer.data()); 12 | auto* frame = reinterpret_cast(buffer.data() + sizeof(std::uint64_t)); 13 | emit received_frame(*time, *frame); 14 | } 15 | else if (buffer.size() == sizeof(tin::Can_raw_frame)) { 16 | // Time may be imprecise and fluctuate +-15 ms on every second full moon 17 | auto time = util::Timer::high_res_now(); 18 | auto* frame = reinterpret_cast(buffer.data()); 19 | emit received_frame(time, *frame); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/network/canudpreceiver.h: -------------------------------------------------------------------------------- 1 | #ifndef NETWORK_CANUDPRECEIVER_H 2 | #define NETWORK_CANUDPRECEIVER_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tincan/canrawframe.h" 10 | #include "network/udpasyncreceiver.h" 11 | 12 | 13 | namespace network { 14 | 15 | 16 | class Can_udp_receiver final : public QObject, public udp::Async_receiver 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | Can_udp_receiver() = default; 22 | Can_udp_receiver(const Can_udp_receiver&) = delete; 23 | Can_udp_receiver(Can_udp_receiver&&) = delete; 24 | Can_udp_receiver& operator=(const Can_udp_receiver&) = delete; 25 | Can_udp_receiver& operator=(Can_udp_receiver&&) = delete; 26 | 27 | void handle_receive(gsl::span buffer) override; 28 | 29 | signals: 30 | void received_frame(std::uint64_t, tin::Can_raw_frame); 31 | }; 32 | 33 | 34 | } // namespace network 35 | 36 | 37 | #endif // NETWORK_CANUDPRECEIVER_H 38 | -------------------------------------------------------------------------------- /src/network/dummyreceiver.h: -------------------------------------------------------------------------------- 1 | #ifndef NETWORK_DUMMYRECEIVER_H 2 | #define NETWORK_DUMMYRECEIVER_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "tincan/canrawframe.h" 11 | #include "util.h" 12 | 13 | 14 | namespace network { 15 | 16 | 17 | // 1. Add receiver as Main_window class member 18 | 19 | // 2. Connect receive signal to Can_bus slot 20 | // connect(&dummy_receiver_, &network::Dummy_receiver::received_frame, 21 | // &can_bus_, &tin::Can_bus::add_frame, Qt::QueuedConnection); 22 | 23 | // 3. Call start() on button press or startup 24 | 25 | 26 | class Dummy_receiver final : public QObject 27 | { 28 | Q_OBJECT 29 | 30 | public: 31 | void start() 32 | { 33 | if (!running_.load()) 34 | std::thread{&network::Dummy_receiver::run, this}.detach(); 35 | } 36 | 37 | void run() 38 | { 39 | running_.store(true); 40 | 41 | // Setup and start actual CAN receiver/interface 42 | // ... 43 | 44 | while (running_.load()) { 45 | // Poll, wait, etc. for data 46 | // ... 47 | 48 | // Copy or reinterpret data to tin::Can_raw_frame 49 | tin::Can_raw_frame frame; 50 | 51 | // Get timestamp (preferably from receiver) 52 | std::uint64_t time = util::Timer::high_res_now(); 53 | 54 | // Emit signal 55 | emit received_frame(time, frame); 56 | } 57 | } 58 | 59 | void stop() { 60 | running_.store(false); 61 | } 62 | 63 | signals: 64 | void received_frame(std::uint64_t, tin::Can_raw_frame); 65 | 66 | private: 67 | std::atomic running_ = false; 68 | }; 69 | 70 | 71 | } // namespace network 72 | 73 | 74 | #endif // NETWORK_DUMMYRECEIVER_H 75 | -------------------------------------------------------------------------------- /src/network/udpasyncreceiver.cpp: -------------------------------------------------------------------------------- 1 | #include "udpasyncreceiver.h" 2 | 3 | 4 | #include 5 | #include "network/udpsocketguard.h" 6 | 7 | 8 | network::udp::Async_receiver::~Async_receiver() 9 | { 10 | stop(); 11 | } 12 | 13 | 14 | void network::udp::Async_receiver::stop() 15 | { 16 | // Cancel async handlers 17 | if (is_running()) 18 | socket_.cancel(); 19 | } 20 | 21 | 22 | void network::udp::Async_receiver::start(const std::string& ip, std::uint16_t port) 23 | { 24 | if (is_running()) 25 | return; 26 | 27 | Socket_guard socket_guard{socket_}; 28 | try { 29 | socket_.open(asio::ip::udp::v4()); 30 | asio::ip::udp::resolver resolver{io_service_}; 31 | asio::ip::udp::resolver::query query{asio::ip::udp::v4(), ip.c_str(), 32 | std::to_string(port).c_str()}; 33 | asio::ip::udp::endpoint ep = *resolver.resolve(query); 34 | socket_.bind(ep); 35 | std::cout << "Listening on " << ep.address().to_string() << ":" << ep.port() << std::endl; 36 | } 37 | catch (asio::system_error& e) { 38 | std::cerr << "UDP socket error:\n" << e.what() << std::endl; 39 | return; 40 | } 41 | 42 | if (io_service_.stopped()) 43 | io_service_.reset(); 44 | 45 | start_async_receive(); // Queue some work for the io_service 46 | io_service_.run(); // Block until all work is done 47 | std::cout << "Receiver stopped" << std::endl; 48 | } 49 | 50 | 51 | void network::udp::Async_receiver::start_async_receive() 52 | { 53 | // Start async receives until stop() is called 54 | socket_.async_receive(asio::buffer(buffer_), 55 | [this](const asio::error_code& error, std::size_t n) { 56 | if (!error && n > 0) { 57 | handle_receive(gsl::span{buffer_}.first(n)); 58 | } 59 | if (!error) 60 | start_async_receive(); // Prevent io_service from stopping 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /src/network/udpasyncreceiver.h: -------------------------------------------------------------------------------- 1 | #ifndef NETWORK_UDP_ASYNCRECEIVER_H 2 | #define NETWORK_UDP_ASYNCRECEIVER_H 3 | 4 | 5 | /* Base class for implementing an asynchronous UDP receiver. A derived class must implement 6 | handle_receive which gets passed a view of the received data. The function start() will 7 | block and should be called in its own thread. */ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | 18 | namespace network::udp { 19 | 20 | 21 | class Async_receiver 22 | { 23 | public: 24 | Async_receiver() : socket_{io_service_} {} 25 | virtual ~Async_receiver(); 26 | Async_receiver(const Async_receiver&) = delete; 27 | Async_receiver& operator=(const Async_receiver&) = delete; 28 | 29 | void start(const std::string& ip, std::uint16_t port); 30 | void stop(); 31 | bool is_running() const { return socket_.is_open(); } 32 | 33 | protected: 34 | virtual void handle_receive(gsl::span buffer) = 0; 35 | 36 | private: 37 | void start_async_receive(); 38 | 39 | asio::io_service io_service_; 40 | asio::ip::udp::socket socket_; 41 | std::array buffer_; 42 | }; 43 | 44 | 45 | } // namespace network::udp 46 | 47 | 48 | #endif // NETWORK_UDP_ASYNCRECEIVER_H 49 | -------------------------------------------------------------------------------- /src/network/udpsocketguard.h: -------------------------------------------------------------------------------- 1 | #ifndef NETWORK_UDP_SOCKETGUARD_H 2 | #define NETWORK_UDP_SOCKETGUARD_H 3 | 4 | 5 | /* RAII wrapper for an ASIO UDP socket */ 6 | 7 | 8 | #include 9 | 10 | 11 | namespace network::udp { 12 | 13 | 14 | class Socket_guard final 15 | { 16 | public: 17 | explicit Socket_guard(asio::ip::udp::socket& s) : socket_{s} { } 18 | ~Socket_guard() { if (socket_.is_open() && !is_released_) socket_.close(); } 19 | Socket_guard(const Socket_guard&) = delete; 20 | Socket_guard& operator=(const Socket_guard&) = delete; 21 | void release() { is_released_ = true; } 22 | bool has_open_socket() const { return socket_.is_open(); } 23 | 24 | private: 25 | bool is_released_ = false; 26 | asio::ip::udp::socket& socket_; 27 | }; 28 | 29 | 30 | } // namespace network::udp 31 | 32 | 33 | #endif // NETWORK_UDP_SOCKETGUARD_H 34 | -------------------------------------------------------------------------------- /src/tincan/canbus.cpp: -------------------------------------------------------------------------------- 1 | #include "canbus.h" 2 | 3 | 4 | #include 5 | 6 | #include "tincan/canbusdef.h" 7 | #include "tincan/signalutil.h" 8 | #include "util.h" 9 | 10 | 11 | namespace { 12 | 13 | 14 | void calculate_signal_values(tin::Can_frame& frame) 15 | { 16 | if (frame.frame_def->multiplexer) { 17 | std::int32_t switch_value = -1; 18 | auto it = std::find_if(std::begin(frame.can_signals), std::end(frame.can_signals), 19 | [&](auto&& s) { return s.signal_def->multiplex_switch; }); 20 | if (it != std::end(frame.can_signals)) { 21 | const auto* def = it->signal_def; 22 | auto raw = tin::build_raw_value(frame.raw_data, def->pos, def->len, def->order, def->sign); 23 | switch_value = static_cast(std::get(raw)); 24 | } 25 | for (auto& signal : frame.can_signals) { 26 | const auto* def = signal.signal_def; 27 | if (def->multiplex_value == -1 || def->multiplex_value == switch_value) { 28 | signal.raw = tin::build_raw_value(frame.raw_data, def->pos, def->len, def->order, def->sign); 29 | signal.phys = tin::calc_phys_value(signal.raw, def->factor, def->offset); 30 | } 31 | } 32 | } 33 | else { 34 | for (auto& signal : frame.can_signals) { 35 | const auto* def = signal.signal_def; 36 | signal.raw = tin::build_raw_value(frame.raw_data, def->pos, def->len, def->order, def->sign); 37 | signal.phys = tin::calc_phys_value(signal.raw, def->factor, def->offset); 38 | } 39 | } 40 | } 41 | 42 | 43 | } // namespace 44 | 45 | 46 | void tin::Can_bus::reset() 47 | { 48 | set_definition(nullptr); 49 | reset_frames(); 50 | } 51 | 52 | 53 | const tin::Can_frame* tin::Can_bus::frame(std::uint32_t id) const 54 | { 55 | auto it = frames_.find(id); 56 | if (it != std::end(frames_)) 57 | return &it->second; 58 | return nullptr; 59 | } 60 | 61 | 62 | void tin::Can_bus::add_frame(std::uint64_t time, tin::Can_raw_frame raw_frame) 63 | { 64 | auto it = frames_.find(raw_frame.id); 65 | if (it != std::end(frames_)) { // Frame was received previously, update data 66 | auto& frame = it->second; 67 | frame.raw_data = raw_frame.data; 68 | frame.receive_time = time; 69 | frame.last_receive_system_time = util::Timer::system_now(); 70 | frame.alive = true; 71 | auto& prev_time = prev_frame_time_[frame.id]; 72 | auto& cycle_times = cycle_times_[frame.id]; 73 | cycle_times.push_back(static_cast(time - prev_time)); 74 | frame.mean_cycle_time = util::math::mean(cycle_times); 75 | prev_time = time; 76 | if (frame.frame_def) 77 | calculate_signal_values(frame); 78 | } 79 | else { // A new frame, add to bus 80 | Can_frame frame; 81 | frame.id = raw_frame.id; 82 | frame.length = raw_frame.dlc; 83 | frame.raw_data = raw_frame.data; 84 | frame.receive_time = time; 85 | frame.last_receive_system_time = util::Timer::system_now(); 86 | frame.alive = true; 87 | prev_frame_time_[frame.id] = time; 88 | cycle_times_[frame.id] = boost::circular_buffer{20}; 89 | if (bus_def_) { 90 | if (frame.frame_def = find_frame_def(*bus_def_, raw_frame.id); frame.frame_def) { 91 | for (const auto& signal_def : frame.frame_def->can_signal_defs) { 92 | frame.can_signals.emplace_back(); 93 | frame.can_signals.back().signal_def = &signal_def; 94 | } 95 | calculate_signal_values(frame); 96 | } 97 | } 98 | frames_[frame.id] = frame; 99 | } 100 | 101 | emit data_changed(raw_frame.id); 102 | } 103 | 104 | 105 | void tin::Can_bus::update_frames() 106 | { 107 | auto now = util::Timer::system_now(); 108 | for (auto& p : frames_) { 109 | auto& frame = p.second; 110 | if (frame.alive) { 111 | auto delta = now - frame.last_receive_system_time; 112 | if (frame.mean_cycle_time > 0 && delta > frame.mean_cycle_time * 3) { 113 | frame.alive = false; 114 | emit data_changed(frame.id); 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/tincan/canbus.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANBUS_H 2 | #define TIN_CANBUS_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | 13 | #include "tincan/canframe.h" 14 | #include "tincan/canrawframe.h" 15 | #include "tincan/cansignal.h" 16 | 17 | 18 | namespace tin { 19 | 20 | 21 | struct Can_bus_def; 22 | 23 | 24 | class Can_bus : public QObject 25 | { 26 | Q_OBJECT 27 | 28 | public: 29 | void set_definition(const Can_bus_def* bus_def) { bus_def_ = bus_def; } 30 | void reset_frames() { frames_.clear(); } 31 | const tin::Can_frame* frame(std::uint32_t id) const; 32 | 33 | signals: 34 | void data_changed(std::uint32_t); 35 | 36 | public slots: 37 | void reset(); 38 | void add_frame(std::uint64_t time, tin::Can_raw_frame raw_frame); 39 | void update_frames(); 40 | 41 | private: 42 | const Can_bus_def* bus_def_ = nullptr; 43 | std::unordered_map frames_; 44 | std::unordered_map prev_frame_time_; 45 | std::unordered_map> cycle_times_; 46 | }; 47 | 48 | 49 | } // namespace tin 50 | 51 | 52 | #endif // TIN_CANBUS_H 53 | -------------------------------------------------------------------------------- /src/tincan/canbusdef.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANBUSDEF_H 2 | #define TIN_CANBUSDEF_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tincan/canframedef.h" 10 | 11 | 12 | namespace tin { 13 | 14 | 15 | struct Can_bus_def 16 | { 17 | std::string source_name; 18 | std::vector nodes; 19 | std::vector frame_defs; 20 | }; 21 | 22 | 23 | inline const Can_frame_def* find_frame_def(const Can_bus_def& bus_def, std::uint32_t id) 24 | { 25 | auto it = std::find_if(std::begin(bus_def.frame_defs), std::end(bus_def.frame_defs), 26 | [id](const auto& fd){ return fd.id == id; }); 27 | if (it != std::end(bus_def.frame_defs)) 28 | return &*it; 29 | return nullptr; 30 | } 31 | 32 | 33 | inline const Can_frame_def* find_frame_def(const Can_bus_def& bus_def, std::string_view name) 34 | { 35 | auto it = std::find_if(std::begin(bus_def.frame_defs), std::end(bus_def.frame_defs), 36 | [name](const auto& fd){ return fd.name == name; }); 37 | if (it != std::end(bus_def.frame_defs)) 38 | return &*it; 39 | return nullptr; 40 | } 41 | 42 | 43 | } // namespace tin 44 | 45 | 46 | #endif // TIN_CANBUSDEF_H 47 | -------------------------------------------------------------------------------- /src/tincan/canframe.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANFRAME_H 2 | #define TIN_CANFRAME_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tincan/cansignal.h" 10 | #include "tincan/canframedef.h" 11 | 12 | 13 | namespace tin { 14 | 15 | 16 | struct Can_frame 17 | { 18 | std::uint32_t id = 0; 19 | std::int32_t mean_cycle_time = 0; 20 | std::uint64_t receive_time = 0; 21 | std::int64_t last_receive_system_time = 0; 22 | std::array raw_data; 23 | std::uint8_t length = 0; 24 | std::vector can_signals; 25 | const Can_frame_def* frame_def = nullptr; 26 | bool alive = false; 27 | }; 28 | 29 | 30 | } // namespace tin 31 | 32 | 33 | #endif // TIN_CANFRAME_H 34 | -------------------------------------------------------------------------------- /src/tincan/canframedef.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANFRAMEDEF_H 2 | #define TIN_CANFRAMEDEF_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tincan/cansignaldef.h" 10 | 11 | 12 | namespace tin { 13 | 14 | 15 | struct Can_frame_def 16 | { 17 | std::uint32_t id; 18 | std::string name; 19 | std::uint32_t dlc; 20 | std::string transmitter; 21 | bool multiplexer; 22 | std::vector can_signal_defs; 23 | }; 24 | 25 | 26 | } // namespace tin 27 | 28 | 29 | #endif // TIN_CANFRAMEDEF_H 30 | -------------------------------------------------------------------------------- /src/tincan/canrawframe.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANRAWFRAME_H 2 | #define TIN_CANRAWFRAME_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | 9 | namespace tin { 10 | 11 | 12 | struct Can_raw_frame 13 | { 14 | std::uint32_t id; 15 | std::uint8_t dlc; 16 | alignas(8) std::array data; 17 | }; 18 | 19 | 20 | } // namespace tin 21 | 22 | 23 | #endif // TIN_CANRAWFRAME_H 24 | -------------------------------------------------------------------------------- /src/tincan/cansignal.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANSIGNAL_H 2 | #define TIN_CANSIGNAL_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "tincan/cansignaldef.h" 9 | 10 | 11 | namespace tin { 12 | 13 | 14 | struct Can_signal 15 | { 16 | std::variant raw; 17 | std::variant phys; 18 | const Can_signal_def* signal_def; 19 | }; 20 | 21 | 22 | } // namespace tin 23 | 24 | 25 | #endif // TIN_CANSIGNAL_H 26 | -------------------------------------------------------------------------------- /src/tincan/cansignaldef.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANSIGNALDEF_H 2 | #define TIN_CANSIGNALDEF_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | namespace tin { 12 | 13 | 14 | using Value_map = std::map; 15 | 16 | 17 | enum class Byte_order : std::uint8_t { Intel, Moto }; 18 | enum class Value_sign : std::uint8_t { Signed, Unsigned }; 19 | 20 | 21 | struct Can_signal_meta_data 22 | { 23 | std::int8_t factor_precision = 7; 24 | std::int8_t offset_precision = 7; 25 | std::int8_t minimum_precision = 7; 26 | std::int8_t maximum_precision = 7; 27 | }; 28 | 29 | 30 | struct Can_signal_def 31 | { 32 | bool multiplex_switch; 33 | Byte_order order; 34 | Value_sign sign; 35 | std::int32_t multiplex_value; 36 | std::uint32_t pos; 37 | std::uint32_t len; 38 | double factor; 39 | double offset; 40 | double minimum; 41 | double maximum; 42 | std::string unit; 43 | std::string name; 44 | std::vector receiver; 45 | Value_map value_definitions; 46 | Can_signal_meta_data meta_data; 47 | }; 48 | 49 | 50 | } // namespace tin 51 | 52 | 53 | #endif // TIN_CANSIGNALDEF_H 54 | -------------------------------------------------------------------------------- /src/tincan/cantracer.cpp: -------------------------------------------------------------------------------- 1 | #include "cantracer.h" 2 | 3 | 4 | #include "fmt/fmtlib.h" 5 | 6 | #include "tincan/cansignal.h" 7 | #include "tincan/canframe.h" 8 | #include "tincan/canbus.h" 9 | 10 | 11 | namespace { 12 | 13 | 14 | using namespace std::string_literals; 15 | 16 | 17 | const auto silver = "#C0C0C0"s; 18 | const auto magenta = "#FB4FE7"s; 19 | 20 | 21 | const auto frame_format = R"(

{},{:>4},)" 22 | R"({:X},)" 23 | R"({},)" 24 | R"({:0>2X})" 25 | R"({:0>2X})" 26 | R"({:0>2X})" 27 | R"({:0>2X})" 28 | R"({:0>2X})" 29 | R"({:0>2X})" 30 | R"({:0>2X})" 31 | R"({:0>2X})" 32 | R"(,{}

)"; 33 | 34 | const auto signal_format = R"(

{},{:>4},)" 35 | R"({},)" 36 | R"({:>10},)" 37 | R"({}

)"; 38 | 39 | 40 | std::string to_string(const tin::Can_frame* frame, const tin::Can_frame* prev_frame) 41 | { 42 | return fmt::format(frame_format, 43 | frame->receive_time, 44 | prev_frame->receive_time > 0 ? frame->receive_time - prev_frame->receive_time : 0, 45 | frame->id, 46 | frame->frame_def ? frame->frame_def->dlc : 8, 47 | frame->raw_data[7] != prev_frame->raw_data[7] ? magenta : silver, 48 | frame->raw_data[7], 49 | frame->raw_data[6] != prev_frame->raw_data[6] ? magenta : silver, 50 | frame->raw_data[6], 51 | frame->raw_data[5] != prev_frame->raw_data[5] ? magenta : silver, 52 | frame->raw_data[5], 53 | frame->raw_data[4] != prev_frame->raw_data[4] ? magenta : silver, 54 | frame->raw_data[4], 55 | frame->raw_data[3] != prev_frame->raw_data[3] ? magenta : silver, 56 | frame->raw_data[3], 57 | frame->raw_data[2] != prev_frame->raw_data[2] ? magenta : silver, 58 | frame->raw_data[2], 59 | frame->raw_data[1] != prev_frame->raw_data[1] ? magenta : silver, 60 | frame->raw_data[1], 61 | frame->raw_data[0] != prev_frame->raw_data[0] ? magenta : silver, 62 | frame->raw_data[0], 63 | frame->frame_def ? frame->frame_def->name : "FRAME"); 64 | } 65 | 66 | 67 | std::string to_string(const tin::Can_frame* frame, const tin::Can_frame* prev_frame, 68 | const tin::Can_signal* signal) 69 | { 70 | std::string physical_value; 71 | if (std::holds_alternative(signal->phys)) 72 | physical_value = fmt::format("{:>10}", std::get(signal->phys)); 73 | else if (std::holds_alternative(signal->phys)) 74 | physical_value = fmt::format("{:>10}", std::get(signal->phys)); 75 | else 76 | physical_value = fmt::format("{:>10.{}f}", std::get(signal->phys), 77 | signal->signal_def->meta_data.factor_precision); 78 | 79 | return fmt::format(signal_format, 80 | frame->receive_time, 81 | prev_frame->receive_time > 0 ? frame->receive_time - prev_frame->receive_time : 0, 82 | physical_value, 83 | std::holds_alternative(signal->raw) ? std::get(signal->raw) : 84 | std::get(signal->raw), 85 | signal->signal_def->name); 86 | } 87 | 88 | 89 | } // namespace 90 | 91 | 92 | void tin::Can_tracer::update_data(std::uint32_t id) 93 | { 94 | if (trace_frame_ && !paused_ && id == trace_frame_->id) { 95 | QString line; 96 | if (trace_signal_) 97 | line = QString::fromStdString(to_string(trace_frame_, &prev_frame_, trace_signal_)); 98 | else 99 | line = QString::fromStdString(to_string(trace_frame_, &prev_frame_)); 100 | prev_frame_ = *trace_frame_; 101 | emit line_ready(line); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/tincan/cantracer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_CANTRACER_H 2 | #define TIN_CANTRACER_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "tincan/canframe.h" 11 | 12 | 13 | namespace tin { 14 | 15 | 16 | class Can_signal; 17 | 18 | 19 | class Can_tracer : public QObject 20 | { 21 | Q_OBJECT 22 | 23 | public: 24 | void set_frame(const Can_frame* trace_frame) { trace_frame_ = trace_frame; 25 | prev_frame_.receive_time = 0; trace_signal_ = nullptr; } 26 | void set_signal(const Can_signal* trace_signal, const Can_frame* trace_frame) { 27 | trace_signal_ = trace_signal; trace_frame_ = trace_frame; prev_frame_.receive_time = 0; } 28 | 29 | signals: 30 | void line_ready(const QString&); 31 | 32 | public slots: 33 | void update_data(std::uint32_t id); 34 | void set_paused(bool paused) { paused_ = paused; } 35 | void reset() { set_frame(nullptr); } 36 | 37 | private: 38 | const Can_frame* trace_frame_ = nullptr; 39 | const Can_signal* trace_signal_ = nullptr; 40 | Can_frame prev_frame_; 41 | bool paused_ = false; 42 | }; 43 | 44 | 45 | } // namespace tin 46 | 47 | 48 | #endif // TIN_CANTRACER_H 49 | -------------------------------------------------------------------------------- /src/tincan/errors.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_ERRORS_H 2 | #define TIN_ERRORS_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | 9 | namespace tin { 10 | 11 | 12 | class File_error : public std::runtime_error 13 | { 14 | public: 15 | explicit File_error(const std::string& s) : std::runtime_error{s} {} 16 | explicit File_error(const char* s) : std::runtime_error{s} {} 17 | }; 18 | 19 | 20 | } // namespace tin 21 | 22 | 23 | #endif // TIN_ERRORS_H 24 | -------------------------------------------------------------------------------- /src/tincan/helper.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_HELPER_H 2 | #define TIN_HELPER_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tincan/cansignal.h" 10 | #include "tincan/canframe.h" 11 | 12 | 13 | namespace tin { 14 | 15 | 16 | inline const Can_signal* find_signal(const Can_frame* frame, std::string_view name) 17 | { 18 | if (!frame) 19 | return nullptr; 20 | 21 | auto it = std::find_if(std::begin(frame->can_signals), std::end(frame->can_signals), 22 | [name](const auto& s){ return s.signal_def && s.signal_def->name == name; }); 23 | if (it != std::end(frame->can_signals)) 24 | return &*it; 25 | return nullptr; 26 | } 27 | 28 | 29 | } // namespace tin 30 | 31 | 32 | #endif // TIN_HELPER_H 33 | -------------------------------------------------------------------------------- /src/tincan/signalutil.cpp: -------------------------------------------------------------------------------- 1 | #include "signalutil.h" 2 | 3 | 4 | #include 5 | #include 6 | 7 | #ifdef _MSC_VER 8 | #include 9 | #endif 10 | 11 | #include "tincan/cansignaldef.h" 12 | 13 | 14 | namespace { 15 | 16 | 17 | inline bool is_fraction(double val) 18 | { 19 | double integral; 20 | auto fractional = std::modf(val, &integral); 21 | return std::abs(fractional) != 0; 22 | } 23 | 24 | 25 | template tin::phys_val calc_phys(T val, double factor, double offset) 26 | { 27 | // Nothing to be done, raw and physical value are identical 28 | if (factor == 1 && offset == 0) 29 | return val; 30 | 31 | // We need a floating point value to represent the result 32 | if (is_fraction(factor) || is_fraction(offset)) 33 | return static_cast(val) * factor + offset; 34 | 35 | // Let's squeeze the result into an integer and hope it fits (sue me) 36 | return static_cast(val * factor + offset); 37 | } 38 | template tin::phys_val calc_phys(std::uint64_t, double, double); 39 | template tin::phys_val calc_phys(std::int64_t, double, double); 40 | 41 | 42 | } // namespace 43 | 44 | 45 | tin::raw_val tin::build_raw_value(const std::array& buffer, std::uint32_t pos, 46 | std::uint32_t len, Byte_order order, Value_sign sign) 47 | { 48 | std::uint64_t raw; 49 | std::memcpy(&raw, &buffer, sizeof(raw)); 50 | 51 | if (order == tin::Byte_order::Moto) { 52 | #ifdef __GNUC__ 53 | raw = __builtin_bswap64(raw); 54 | #elif _MSC_VER 55 | raw = _byteswap_uint64(raw); 56 | #else 57 | #error "Byte swap required" 58 | #endif 59 | pos = pswap64(pos) - len + 1; // Set position to least significant bit 60 | } 61 | 62 | // Using unsigned since shift operations on signed values aren't well-defined 63 | raw <<= 64 - (pos + len); 64 | bool is_negative = raw & (1ull << 63); 65 | if (sign == tin::Value_sign::Signed && is_negative) 66 | raw = ~raw; // Prepare negative values for zero-insertions due to right shift 67 | raw >>= 64 - len; 68 | if (sign == tin::Value_sign::Signed) { 69 | if (is_negative) 70 | raw = ~raw; // Flipping back to proper value (all inserted zeros are now one) 71 | std::int64_t raw_signed; 72 | std::memcpy(&raw_signed, &raw, sizeof(raw_signed)); 73 | return raw_signed; 74 | } 75 | return raw; 76 | } 77 | 78 | 79 | tin::phys_val tin::calc_phys_value(raw_val raw, double factor, double offset) 80 | { 81 | if (std::holds_alternative(raw)) 82 | return calc_phys(std::get(raw), factor, offset); 83 | return calc_phys(std::get(raw), factor, offset); 84 | } 85 | -------------------------------------------------------------------------------- /src/tincan/signalutil.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_SIGNALUTIL_H 2 | #define TIN_SIGNALUTIL_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tincan/cansignaldef.h" 10 | 11 | 12 | namespace tin { 13 | 14 | 15 | using raw_val = std::variant; 16 | using phys_val = std::variant; 17 | 18 | 19 | inline std::uint32_t pswap64(std::uint32_t pos) 20 | { 21 | // Recalculate bit position after bytes were swapped by __builtin_bswap64 22 | return 64ul - ((pos / 8 + 1) * 8) + (pos % 8); 23 | } 24 | 25 | raw_val build_raw_value(const std::array& raw_bytes, std::uint32_t pos, 26 | std::uint32_t len, Byte_order order, Value_sign sign); 27 | 28 | phys_val calc_phys_value(raw_val raw, double factor, double offset); 29 | 30 | 31 | } // namespace tin 32 | 33 | 34 | #endif // TIN_SIGNALUTIL_H 35 | -------------------------------------------------------------------------------- /src/tincan/simulator.cpp: -------------------------------------------------------------------------------- 1 | #include "simulator.h" 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "util.h" 9 | 10 | 11 | namespace { 12 | 13 | 14 | struct Test_frame 15 | { 16 | std::uint8_t crc; 17 | std::uint8_t alive : 4; 18 | std::uint8_t counter_0 : 4; 19 | std::uint8_t counter_1; 20 | std::uint8_t sine_0; 21 | std::uint8_t sine_1 : 3; 22 | std::uint8_t dummy_0 : 1; 23 | std::uint8_t light : 1; 24 | std::uint8_t color : 3; 25 | std::uint8_t brightness : 7; 26 | std::uint8_t dummy_1 : 1; 27 | std::uint16_t altitude; 28 | }; 29 | 30 | 31 | struct Flux 32 | { 33 | std::uint32_t power_level; 34 | std::uint32_t dispersal_rate; 35 | }; 36 | 37 | 38 | struct Date_time 39 | { 40 | std::uint8_t time_type : 2; 41 | std::uint8_t year_0 : 6; 42 | std::uint8_t year_1; 43 | std::uint8_t month : 4; 44 | std::uint8_t day_0 : 4; 45 | std::uint8_t day_1 : 1; 46 | std::uint8_t am_pm : 1; 47 | std::uint8_t hour : 4; 48 | std::uint8_t minute_0 : 2; 49 | std::uint8_t minute_1 : 4; 50 | }; 51 | 52 | 53 | std::uint8_t calculate_crc(std::uint8_t* data, int length) 54 | { 55 | std::uint8_t crc = 0; 56 | std::uint8_t ext; 57 | std::uint8_t sum; 58 | for (int i=0; i0; --j) { 61 | sum = (crc ^ ext) & 0x01; 62 | crc >>= 1; 63 | if (sum) 64 | crc ^= 0x8C; 65 | ext >>= 1; 66 | } 67 | data++; 68 | } 69 | return crc; 70 | } 71 | 72 | 73 | } // namespace 74 | 75 | 76 | void tin::Simulator::start() 77 | { 78 | if (!running_.load()) 79 | std::thread{&tin::Simulator::run, this}.detach(); 80 | } 81 | 82 | 83 | void tin::Simulator::run() 84 | { 85 | running_.store(true); 86 | 87 | tin::Can_raw_frame test_frame; 88 | test_frame.id = 100; 89 | test_frame.dlc = 8; 90 | auto* test_frame_data = reinterpret_cast(test_frame.data.data()); 91 | test_frame_data->light = 1; 92 | test_frame_data->color = 4; 93 | test_frame_data->brightness = 75; 94 | test_frame_data->altitude = 41337; 95 | std::uint16_t counter = 0; 96 | std::uint32_t angle = 0; 97 | 98 | tin::Can_raw_frame flux; 99 | flux.id = 233; 100 | flux.dlc = 6; 101 | auto* flux_data = reinterpret_cast(flux.data.data()); 102 | flux_data->power_level = 1210000000; 103 | flux_data->dispersal_rate = 7743; 104 | 105 | tin::Can_raw_frame date_time; 106 | date_time.id = 245; 107 | date_time.dlc = 5; 108 | auto* date_time_data = reinterpret_cast(date_time.data.data()); 109 | #pragma GCC diagnostic push 110 | #pragma GCC diagnostic ignored "-Woverflow" 111 | date_time_data->year_0 = 1955; 112 | date_time_data->year_1 = 1955 >> 6; 113 | date_time_data->month = 11; 114 | date_time_data->day_0 = 5; 115 | date_time_data->day_1 = 5 >> 4; 116 | date_time_data->hour = 6; 117 | date_time_data->minute_0 = 31; 118 | date_time_data->minute_1 = 31 >> 2; 119 | #pragma GCC diagnostic pop 120 | 121 | std::mt19937_64 engine(0xE3E00799540DE706); 122 | std::uniform_int_distribution dist{1, 256}; 123 | 124 | while (running_.load()) { 125 | using namespace std::chrono; 126 | { 127 | util::Timer delta_timer{true}; 128 | std::this_thread::sleep_for(milliseconds{20}); 129 | auto delta = delta_timer.stop_milliseconds(); 130 | accumulator1_ += delta; 131 | accumulator2_ += delta; 132 | accumulator3_ += delta; 133 | } 134 | 135 | if (accumulator1_ >= 200) { 136 | accumulator1_ = 0; 137 | test_frame_data->alive++; 138 | counter++; 139 | test_frame_data->counter_0 = static_cast(counter); 140 | test_frame_data->counter_1 = static_cast(counter >> 4); 141 | angle += 5; 142 | float sine = std::sin(angle * 3.14159f / 180.0f); 143 | std::uint16_t sine_raw = static_cast((sine + 1.0f) * 1000.0f + 0.5f); 144 | test_frame_data->sine_0 = static_cast(sine_raw); 145 | test_frame_data->sine_1 = static_cast(sine_raw >> 8); 146 | test_frame_data->crc = calculate_crc(test_frame.data.data() + 1, test_frame.dlc - 1); 147 | auto time = util::Timer::high_res_now(); 148 | emit received_frame(time, test_frame); 149 | } 150 | 151 | if (accumulator2_ >= 100) { 152 | accumulator2_ = 0; 153 | flux_data->dispersal_rate = static_cast(7743 + dist(engine) - 128); 154 | auto time = util::Timer::high_res_now(); 155 | emit received_frame(time, flux); 156 | } 157 | 158 | if (accumulator3_ >= 1000) { 159 | accumulator3_ = 0; 160 | auto time = util::Timer::high_res_now(); 161 | emit received_frame(time, date_time); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/tincan/simulator.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_SIMULATOR_H 2 | #define TIN_SIMULATOR_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "tincan/canrawframe.h" 11 | 12 | 13 | namespace tin { 14 | 15 | 16 | class Simulator final : public QObject 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | Simulator() = default; 22 | Simulator(const Simulator&) = delete; 23 | Simulator(Simulator&&) = delete; 24 | Simulator& operator=(const Simulator&) = delete; 25 | Simulator& operator=(Simulator&&) = delete; 26 | void start(); 27 | void stop() { running_.store(false); } 28 | 29 | signals: 30 | void received_frame(std::uint64_t, tin::Can_raw_frame); 31 | 32 | private: 33 | void run(); 34 | std::atomic running_ = false; 35 | std::int64_t accumulator1_ = 0; 36 | std::int64_t accumulator2_ = 0; 37 | std::int64_t accumulator3_ = 0; 38 | }; 39 | 40 | 41 | } // namespace tin 42 | 43 | 44 | #endif // TIN_SIMULATOR_H 45 | -------------------------------------------------------------------------------- /src/tincan/translate.cpp: -------------------------------------------------------------------------------- 1 | #include "translate.h" 2 | 3 | 4 | #include "tincan/cansignaldef.h" 5 | #include "tincan/canframedef.h" 6 | 7 | 8 | tin::Can_bus_def tin::to_can_bus_def(const dbc::File& file) 9 | { 10 | tin::Can_bus_def bus_def; 11 | bus_def.source_name = file.name; 12 | bus_def.nodes = file.nodes; 13 | 14 | for (const auto& fd : file.frame_defs) { 15 | bus_def.frame_defs.emplace_back(); 16 | auto& frame_def = bus_def.frame_defs.back(); 17 | frame_def.id = fd.id; 18 | frame_def.dlc = fd.dlc; 19 | frame_def.name = fd.name; 20 | frame_def.transmitter = fd.transmitter; 21 | frame_def.multiplexer = fd.multiplexer; 22 | 23 | for (const auto& sd : fd.signal_defs) { 24 | frame_def.can_signal_defs.emplace_back(); 25 | auto& signal_def = frame_def.can_signal_defs.back(); 26 | signal_def.order = sd.order == dbc::Byte_order::Intel ? tin::Byte_order::Intel : 27 | tin::Byte_order::Moto; 28 | signal_def.sign = sd.sign == dbc::Value_sign::Signed ? tin::Value_sign::Signed : 29 | tin::Value_sign::Unsigned; 30 | signal_def.multiplex_switch = sd.multiplex_switch; 31 | signal_def.multiplex_value = sd.multiplex_value; 32 | signal_def.pos = sd.pos; 33 | signal_def.len = sd.len; 34 | signal_def.factor = sd.factor; 35 | signal_def.offset = sd.offset; 36 | signal_def.minimum = sd.minimum; 37 | signal_def.maximum = sd.maximum; 38 | signal_def.unit = sd.unit; 39 | signal_def.name = sd.name; 40 | signal_def.receiver = sd.receiver; 41 | signal_def.value_definitions = sd.value_definitions; 42 | signal_def.meta_data.factor_precision = sd.meta_data.factor_precision; 43 | signal_def.meta_data.offset_precision = sd.meta_data.offset_precision; 44 | signal_def.meta_data.minimum_precision = sd.meta_data.minimum_precision; 45 | signal_def.meta_data.maximum_precision = sd.meta_data.maximum_precision; 46 | } 47 | } 48 | 49 | return bus_def; 50 | } 51 | 52 | 53 | dbc::File tin::to_dbc_file(const tin::Can_bus_def& bus_def) 54 | { 55 | dbc::File dbc_file; 56 | dbc_file.nodes = bus_def.nodes; 57 | 58 | for (const auto& fd : bus_def.frame_defs) { 59 | dbc_file.frame_defs.emplace_back(); 60 | auto& frame_def = dbc_file.frame_defs.back(); 61 | frame_def.id = fd.id; 62 | frame_def.dlc = fd.dlc; 63 | frame_def.name = fd.name; 64 | frame_def.transmitter = fd.transmitter; 65 | frame_def.multiplexer = fd.multiplexer; 66 | 67 | for (const auto& sd : fd.can_signal_defs) { 68 | frame_def.signal_defs.emplace_back(); 69 | auto& signal_def = frame_def.signal_defs.back(); 70 | signal_def.order = sd.order == tin::Byte_order::Intel ? dbc::Byte_order::Intel : 71 | dbc::Byte_order::Moto; 72 | signal_def.sign = sd.sign == tin::Value_sign::Signed ? dbc::Value_sign::Signed : 73 | dbc::Value_sign::Unsigned; 74 | signal_def.multiplex_switch = sd.multiplex_switch; 75 | signal_def.multiplex_value = sd.multiplex_value; 76 | signal_def.pos = sd.pos; 77 | signal_def.len = sd.len; 78 | signal_def.factor = sd.factor; 79 | signal_def.offset = sd.offset; 80 | signal_def.minimum = sd.minimum; 81 | signal_def.maximum = sd.maximum; 82 | signal_def.unit = sd.unit; 83 | signal_def.name = sd.name; 84 | signal_def.receiver = sd.receiver; 85 | signal_def.value_definitions = sd.value_definitions; 86 | signal_def.meta_data.factor_precision = sd.meta_data.factor_precision; 87 | signal_def.meta_data.offset_precision = sd.meta_data.offset_precision; 88 | signal_def.meta_data.minimum_precision = sd.meta_data.minimum_precision; 89 | signal_def.meta_data.maximum_precision = sd.meta_data.maximum_precision; 90 | } 91 | } 92 | 93 | return dbc_file; 94 | } 95 | -------------------------------------------------------------------------------- /src/tincan/translate.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_TRANSLATE_H 2 | #define TIN_TRANSLATE_H 3 | 4 | 5 | // Translate data from source-specific formats to generic types 6 | 7 | 8 | #include "canbusdef.h" 9 | #include "file/dbcfile.h" 10 | 11 | 12 | namespace tin { 13 | 14 | 15 | Can_bus_def to_can_bus_def(const dbc::File& file); 16 | dbc::File to_dbc_file(const tin::Can_bus_def& bus_def); 17 | 18 | 19 | } // namespace tin 20 | 21 | 22 | #endif // TIN_TRANSLATE_H 23 | -------------------------------------------------------------------------------- /src/ui/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "util.h" 21 | #include "tincan/canrawframe.h" 22 | #include "file/dbcparser.h" 23 | #include "file/dbcwriter.h" 24 | #include "file/jsonreader.h" 25 | #include "file/jsonwriter.h" 26 | #include "tincan/errors.h" 27 | #include "tincan/helper.h" 28 | #include "tincan/translate.h" 29 | #include "tincan/cansignaldef.h" 30 | #include "models/treeitemid.h" 31 | #include "models/cansignalitem.h" 32 | #include "models/canframeitem.h" 33 | #include "models/cansignaldefitem.h" 34 | #include "ui/treeviewdialog.h" 35 | 36 | 37 | Q_DECLARE_METATYPE(tin::Can_raw_frame) 38 | 39 | 40 | Main_window::Main_window(QWidget* parent) 41 | : QMainWindow{parent}, ui{new Ui::MainWindow}, can_bus_model_{&can_bus_} 42 | { 43 | ui->setupUi(this); 44 | setWindowTitle("Tincan"); 45 | ui->tabWidgetMain->setCurrentIndex(0); 46 | ui->tabWidgetDetail->setCurrentIndex(0); 47 | 48 | qRegisterMetaType("Can_raw_frame"); 49 | 50 | // Qt Designer bug keeps setting this false 51 | ui->treeFrameView->header()->setVisible(true); 52 | 53 | QFont view_font{"Consolas"}; 54 | QFont trace_font{"Consolas", 10}; 55 | 56 | // Set default value for size hint 57 | can_bus_def_model_.set_row_height(QFontMetrics{view_font}.height() - 4); 58 | 59 | ui->treeFrameView->setModel(&can_bus_model_); 60 | ui->treeViewBusDef->setModel(&can_bus_def_model_); 61 | ui->treeFrameView->setAlternatingRowColors(true); 62 | ui->treeViewBusDef->setAlternatingRowColors(true); 63 | ui->treeViewBusDef->setFont(view_font); 64 | ui->treeFrameView->setFont(view_font); 65 | ui->treeViewBusDef->setUniformRowHeights(true); 66 | ui->treeFrameView->setUniformRowHeights(true); 67 | ui->treeFrameView->setContextMenuPolicy(Qt::CustomContextMenu); 68 | 69 | ui->plainTrace->setLineWrapMode(QPlainTextEdit::NoWrap); 70 | ui->plainTrace->setMaximumBlockCount(256); 71 | ui->plainTrace->setFont(trace_font); 72 | //ui->plainTrace->setCenterOnScroll(true); 73 | 74 | ui->splitter->setStretchFactor(0, 2); 75 | ui->splitter->setStretchFactor(1, 1); 76 | 77 | connect(ui->treeFrameView, &QTreeView::customContextMenuRequested, this, [this]{ 78 | auto index = ui->treeFrameView->currentIndex(); 79 | if (!index.isValid()) 80 | return; 81 | auto* item = static_cast(index.internalPointer()); 82 | 83 | QMenu menu; 84 | QAction trace_frame{"Trace frame"}; 85 | QAction trace_signal{"Trace signal"}; 86 | switch (item->id()) { 87 | case tin::Item_id::Can_frame: { 88 | menu.addAction(&trace_frame); 89 | } 90 | break; 91 | case tin::Item_id::Can_signal: { 92 | menu.addAction(&trace_signal); 93 | } 94 | break; 95 | default: 96 | break; 97 | } 98 | 99 | QAction* action = menu.exec(QCursor::pos()); 100 | if (action == &trace_frame) { 101 | can_tracer_.set_frame(static_cast(item)->frame()); 102 | std::cout << "Tracing frame" << std::endl; 103 | } 104 | else if (action == &trace_signal) { 105 | can_tracer_.set_signal(static_cast(item)->signal(), 106 | static_cast(item->parent())->frame()); 107 | std::cout << "Tracing signal" << std::endl; 108 | } 109 | }); 110 | 111 | connect(ui->treeViewBusDef, &QTreeView::customContextMenuRequested, this, [this]{ 112 | auto index = ui->treeViewBusDef->currentIndex(); 113 | if (!index.isValid()) 114 | return; 115 | 116 | auto* item = static_cast(index.internalPointer()); 117 | if (item->id() != tin::Item_id::Can_signal_def) 118 | return; 119 | 120 | auto* def = static_cast(item)->signal_def(); 121 | if (!def || def->value_definitions.empty()) 122 | return; 123 | 124 | QAction view_value_definitions{"Value definitions"}; 125 | QMenu menu; 126 | menu.addAction(&view_value_definitions); 127 | QAction* action = menu.exec(QCursor::pos()); 128 | 129 | if (action == &view_value_definitions) { 130 | QString title = QString::fromStdString(def->name) + " value definitions"; 131 | auto* dialog = new Tree_view_dialog{title, this}; 132 | dialog->set_data(def->value_definitions); 133 | dialog->show(); 134 | } 135 | }); 136 | 137 | connect(&can_tracer_, &tin::Can_tracer::line_ready, ui->plainTrace, &QPlainTextEdit::appendHtml); 138 | connect(ui->pushClearTrace, &QPushButton::clicked, ui->plainTrace, &QPlainTextEdit::clear); 139 | connect(ui->pushResetTrace, &QPushButton::clicked, &can_tracer_, &tin::Can_tracer::reset); 140 | connect(ui->pushResetTrace, &QPushButton::clicked, ui->plainTrace, &QPlainTextEdit::clear); 141 | connect(ui->checkPauseTrace, &QCheckBox::toggled, &can_tracer_, &tin::Can_tracer::set_paused); 142 | connect(ui->checkPauseTrace, &QCheckBox::toggled, this, [this](bool paused){ 143 | if (paused) ui->plainTrace->appendHtml("Paused"); }); 144 | 145 | connect(ui->pushOpenCloseUdpGateway, &QPushButton::clicked, this, [this]{ 146 | if (can_udp_receiver_.is_running()) { 147 | can_udp_receiver_.stop(); 148 | using namespace std::chrono_literals; 149 | std::this_thread::sleep_for(50ms); 150 | ui->pushOpenCloseUdpGateway->setText("Open"); 151 | reset(); 152 | } 153 | else { 154 | std::thread{&network::Can_udp_receiver::start, &can_udp_receiver_, ui->lineIp->text().toStdString(), 155 | ui->linePort->text().toUShort()}.detach(); 156 | ui->pushOpenCloseUdpGateway->setText("Close"); 157 | } 158 | }); 159 | 160 | connect(ui->pushStartSimulator, &QPushButton::clicked, &simulator_, &tin::Simulator::start); 161 | connect(ui->pushStopSimulator, &QPushButton::clicked, &simulator_, &tin::Simulator::stop); 162 | 163 | connect(&can_udp_receiver_, &network::Can_udp_receiver::received_frame, 164 | &can_bus_, &tin::Can_bus::add_frame, Qt::QueuedConnection); 165 | connect(&simulator_, &tin::Simulator::received_frame, 166 | &can_bus_, &tin::Can_bus::add_frame, Qt::QueuedConnection); 167 | 168 | connect(&update_timer_, &QTimer::timeout, &can_bus_, &tin::Can_bus::update_frames); 169 | update_timer_.start(100); 170 | 171 | connect(&can_bus_, &tin::Can_bus::data_changed, &can_tracer_, &tin::Can_tracer::update_data); 172 | connect(&can_bus_, &tin::Can_bus::data_changed, &can_bus_model_, 173 | &tin::Can_bus_model::update_data_deferred); 174 | 175 | // Handle CAN events 176 | connect(&can_bus_, &tin::Can_bus::data_changed, this, [this](auto frame_id){ 177 | if (auto* frame = can_bus_.frame(frame_id); frame) { 178 | // Do something 179 | } 180 | }); 181 | 182 | connect(ui->pushImportBusDef, &QPushButton::clicked, this, [this]{ 183 | auto filepath = QFileDialog::getOpenFileName(this, tr("Import bus description file"), QString{}, 184 | tr("DBC (*.dbc)")); 185 | try { 186 | util::Timer timer{true}; 187 | dbc_file_ = dbc::parse(filepath.toStdString()); 188 | std::cout << dbc_file_.frame_defs.size() << '\n' << timer.stop_seconds() << std::endl; 189 | // Frames may have pointer to old definition object 190 | can_bus_.reset(); 191 | can_bus_model_.reset(); 192 | can_bus_def_ = tin::to_can_bus_def(dbc_file_); 193 | can_bus_.set_definition(&can_bus_def_); 194 | can_bus_def_model_.set(&can_bus_def_); 195 | for (int i=0; itreeViewBusDef->resizeColumnToContents(i); 197 | ui->lineBusDefFile->setText(QString::fromStdString(can_bus_def_.source_name)); 198 | } 199 | catch (const dbc::Parse_error& e) { 200 | std::cerr << e.what() << std::endl; 201 | } 202 | }); 203 | // Frames may have pointer to old definition object 204 | connect(ui->pushClearBusDef, &QPushButton::clicked, &can_bus_, &tin::Can_bus::reset); 205 | connect(ui->pushClearBusDef, &QPushButton::clicked, &can_bus_model_, &tin::Can_bus_model::reset); 206 | connect(ui->pushClearBusDef, &QPushButton::clicked, 207 | &can_bus_def_model_, &tin::Can_bus_def_model::reset); 208 | connect(ui->pushClearBusDef, &QPushButton::clicked, ui->lineBusDefFile, &QLineEdit::clear); 209 | connect(ui->pushClearBusFrames, &QPushButton::clicked, 210 | &can_bus_model_, &tin::Can_bus_model::reset); 211 | } 212 | 213 | 214 | Main_window::~Main_window() 215 | { 216 | // Stop receiver 217 | if (can_udp_receiver_.is_running()) { 218 | can_udp_receiver_.stop(); 219 | using namespace std::chrono_literals; 220 | std::this_thread::sleep_for(100ms); 221 | } 222 | 223 | delete ui; 224 | } 225 | 226 | 227 | void Main_window::reset() 228 | { 229 | can_bus_.reset_frames(); 230 | can_bus_model_.reset(); 231 | can_tracer_.set_frame(nullptr); 232 | ui->plainTrace->clear(); 233 | } 234 | -------------------------------------------------------------------------------- /src/ui/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_WINDOW_H 2 | #define MAIN_WINDOW_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "network/canudpreceiver.h" 9 | #include "tincan/canbus.h" 10 | #include "tincan/simulator.h" 11 | #include "tincan/canbusdef.h" 12 | #include "tincan/cantracer.h" 13 | #include "file/dbcfile.h" 14 | #include "models/canbusmodel.h" 15 | #include "models/canbusdefmodel.h" 16 | 17 | 18 | namespace Ui { class MainWindow; } 19 | 20 | 21 | class Main_window : public QMainWindow 22 | { 23 | Q_OBJECT 24 | 25 | public: 26 | explicit Main_window(QWidget* parent = nullptr); 27 | ~Main_window(); 28 | 29 | private: 30 | void reset(); 31 | 32 | Ui::MainWindow* ui; 33 | QTimer update_timer_; 34 | network::Can_udp_receiver can_udp_receiver_; 35 | dbc::File dbc_file_; 36 | tin::Can_bus can_bus_; 37 | tin::Simulator simulator_; 38 | tin::Can_bus_def can_bus_def_; 39 | tin::Can_bus_model can_bus_model_; 40 | tin::Can_bus_def_model can_bus_def_model_; 41 | tin::Can_tracer can_tracer_; 42 | }; 43 | 44 | 45 | #endif // MAIN_WINDOW_H 46 | -------------------------------------------------------------------------------- /src/ui/treeviewdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "treeviewdialog.h" 2 | #include "ui_treeviewdialog.h" 3 | 4 | 5 | Tree_view_dialog::Tree_view_dialog(const QString& title, QWidget* parent) 6 | : QDialog{parent}, ui{new Ui::Tree_view_dialog} 7 | { 8 | ui->setupUi(this); 9 | ui->treeView->setModel(&model_); 10 | ui->treeView->header()->setStretchLastSection(true); 11 | setWindowTitle(title); 12 | } 13 | 14 | 15 | Tree_view_dialog::~Tree_view_dialog() 16 | { 17 | delete ui; 18 | } 19 | 20 | 21 | void Tree_view_dialog::set_data(const std::map& data) 22 | { 23 | model_.set(data); 24 | } 25 | -------------------------------------------------------------------------------- /src/ui/treeviewdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef TIN_TREEVIEWDIALOG_H 2 | #define TIN_TREEVIEWDIALOG_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "models/valuedefmodel.h" 12 | 13 | 14 | namespace Ui { class Tree_view_dialog; } 15 | 16 | 17 | class Tree_view_dialog : public QDialog 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | Tree_view_dialog(const QString& title, QWidget* parent = nullptr); 23 | ~Tree_view_dialog(); 24 | void set_data(const std::map& data); 25 | 26 | private: 27 | Ui::Tree_view_dialog* ui = nullptr; 28 | tin::Value_def_model model_; 29 | }; 30 | 31 | 32 | #endif // TIN_TREEVIEWDIALOG_H 33 | -------------------------------------------------------------------------------- /src/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | 4 | util::Timer::Timer(bool start_running) 5 | { 6 | if (start_running) 7 | start(); 8 | } 9 | 10 | 11 | template T util::Timer::system_now() 12 | { 13 | auto d = std::chrono::system_clock::now().time_since_epoch(); 14 | return static_cast(std::chrono::duration_cast(d).count()); 15 | } 16 | template std::int64_t util::Timer::system_now(); 17 | template std::uint64_t util::Timer::system_now(); 18 | 19 | 20 | template T util::Timer::high_res_now() 21 | { 22 | auto d = std::chrono::high_resolution_clock::now().time_since_epoch(); 23 | return static_cast(std::chrono::duration_cast(d).count()); 24 | } 25 | template std::int64_t util::Timer::high_res_now(); 26 | template std::uint64_t util::Timer::high_res_now(); 27 | 28 | 29 | void util::Timer::start() 30 | { 31 | start_time = std::chrono::high_resolution_clock::now(); 32 | } 33 | 34 | 35 | void util::Timer::stop() 36 | { 37 | stop_time = std::chrono::high_resolution_clock::now(); 38 | } 39 | 40 | 41 | float util::Timer::stop_seconds() 42 | { 43 | stop(); 44 | return seconds(); 45 | } 46 | 47 | 48 | std::int64_t util::Timer::stop_milliseconds() 49 | { 50 | stop(); 51 | return milliseconds(); 52 | } 53 | 54 | 55 | float util::Timer::seconds() 56 | { 57 | return std::chrono::duration(stop_time - start_time).count(); 58 | } 59 | 60 | 61 | std::int64_t util::Timer::milliseconds() 62 | { 63 | auto d = stop_time - start_time; 64 | return std::chrono::duration_cast(d).count(); 65 | } 66 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | namespace util { 14 | 15 | 16 | class Timer 17 | { 18 | public: 19 | Timer() = default; 20 | explicit Timer(bool start_running); 21 | ~Timer() = default; 22 | 23 | template static T system_now(); 24 | template static T high_res_now(); 25 | void start(); 26 | void stop(); 27 | float stop_seconds(); 28 | float seconds(); 29 | std::int64_t stop_milliseconds(); 30 | std::int64_t milliseconds(); 31 | 32 | private: 33 | std::chrono::high_resolution_clock::time_point start_time; 34 | std::chrono::high_resolution_clock::time_point stop_time; 35 | }; 36 | 37 | 38 | namespace math { 39 | 40 | 41 | template int mean(T collection) 42 | { 43 | if (std::size(collection) == 0) 44 | return 0; 45 | return std::accumulate(std::begin(collection), std::end(collection), 0) / std::size(collection); 46 | } 47 | 48 | 49 | } // namespace math 50 | 51 | 52 | } // namespace util 53 | 54 | 55 | #endif // UTIL_H 56 | -------------------------------------------------------------------------------- /test/dbcparser_test.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | 3 | 4 | #include "../ext/doctest.h" 5 | #include "../src/file/dbcfile.h" 6 | #include "../src/file/dbcparser.h" 7 | 8 | 9 | using doctest::Approx; 10 | 11 | 12 | TEST_CASE("DBC file parser") 13 | { 14 | auto file = dbc::parse("test.dbc"); 15 | CHECK(file.name == "test.dbc"); 16 | 17 | const auto* veh_state_def = dbc::find_frame_def(file, "VEH_STATE"); 18 | REQUIRE(veh_state_def != nullptr); 19 | CHECK(veh_state_def->dlc == 8); 20 | CHECK(veh_state_def->id == 0xC9); 21 | CHECK(veh_state_def->signal_defs.size() == 4); 22 | 23 | SUBCASE("signal CRC") 24 | { 25 | const auto* crc_def = dbc::find_signal_def(*veh_state_def, "CRC"); 26 | REQUIRE(crc_def != nullptr); 27 | CHECK(crc_def->pos == 0); 28 | CHECK(crc_def->len == 8); 29 | CHECK(crc_def->order == dbc::Byte_order::Intel); 30 | CHECK(crc_def->sign == dbc::Value_sign::Unsigned); 31 | CHECK(Approx(crc_def->factor) == 1); 32 | CHECK(Approx(crc_def->offset) == 0); 33 | CHECK(Approx(crc_def->minimum) == 0); 34 | CHECK(Approx(crc_def->maximum) == 255); 35 | CHECK(crc_def->unit == ""); 36 | CHECK(crc_def->meta_data.factor_precision == 0); 37 | CHECK(crc_def->meta_data.offset_precision == 0); 38 | CHECK(crc_def->meta_data.minimum_precision == 0); 39 | CHECK(crc_def->meta_data.maximum_precision == 0); 40 | } 41 | 42 | SUBCASE("signal ALIV") 43 | { 44 | const auto* aliv_def = dbc::find_signal_def(*veh_state_def, "ALIV"); 45 | REQUIRE(aliv_def != nullptr); 46 | CHECK(aliv_def->pos == 8); 47 | CHECK(aliv_def->len == 4); 48 | CHECK(aliv_def->order == dbc::Byte_order::Intel); 49 | CHECK(aliv_def->sign == dbc::Value_sign::Unsigned); 50 | CHECK(Approx(aliv_def->factor) == 1); 51 | CHECK(Approx(aliv_def->offset) == 0); 52 | CHECK(Approx(aliv_def->minimum) == 0); 53 | CHECK(Approx(aliv_def->maximum) == 15); 54 | CHECK(aliv_def->unit == ""); 55 | CHECK(aliv_def->meta_data.factor_precision == 0); 56 | CHECK(aliv_def->meta_data.offset_precision == 0); 57 | CHECK(aliv_def->meta_data.minimum_precision == 0); 58 | CHECK(aliv_def->meta_data.maximum_precision == 0); 59 | } 60 | 61 | SUBCASE("signal VELOCITY") 62 | { 63 | const auto* velocity_def = dbc::find_signal_def(*veh_state_def, "VELOCITY"); 64 | REQUIRE(velocity_def != nullptr); 65 | CHECK(velocity_def->pos == 12); 66 | CHECK(velocity_def->len == 14); 67 | CHECK(velocity_def->order == dbc::Byte_order::Intel); 68 | CHECK(velocity_def->sign == dbc::Value_sign::Unsigned); 69 | CHECK(Approx(velocity_def->factor) == 0.01); 70 | CHECK(Approx(velocity_def->offset) == 0); 71 | CHECK(Approx(velocity_def->minimum) == 0); 72 | CHECK(Approx(velocity_def->maximum) == 140); 73 | CHECK(velocity_def->unit == "mph"); 74 | CHECK(velocity_def->meta_data.factor_precision == 2); 75 | CHECK(velocity_def->meta_data.offset_precision == 0); 76 | CHECK(velocity_def->meta_data.minimum_precision == 0); 77 | CHECK(velocity_def->meta_data.maximum_precision == 0); 78 | } 79 | 80 | SUBCASE("signal WIPER_POSITION") 81 | { 82 | const auto* wiper_position_def = dbc::find_signal_def(*veh_state_def, "WIPER_POSITION"); 83 | REQUIRE(wiper_position_def != nullptr); 84 | CHECK(wiper_position_def->pos == 26); 85 | CHECK(wiper_position_def->len == 16); 86 | CHECK(wiper_position_def->order == dbc::Byte_order::Intel); 87 | CHECK(wiper_position_def->sign == dbc::Value_sign::Unsigned); 88 | CHECK(Approx(wiper_position_def->factor) == 0.010986328125); 89 | CHECK(Approx(wiper_position_def->offset) == 0); 90 | CHECK(Approx(wiper_position_def->minimum) == 0); 91 | CHECK(Approx(wiper_position_def->maximum) == 180); 92 | CHECK(wiper_position_def->unit == "deg"); 93 | CHECK(wiper_position_def->meta_data.factor_precision == 12); 94 | CHECK(wiper_position_def->meta_data.offset_precision == 0); 95 | CHECK(wiper_position_def->meta_data.minimum_precision == 0); 96 | CHECK(wiper_position_def->meta_data.maximum_precision == 0); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /test/makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CXXFLAGS=-std=c++17 -O3 -Wall 3 | LIBS=-lstdc++fs 4 | INC=-IC:/dev/boost_1_65_1 5 | 6 | 7 | all: dbcparser_test signalutil_test 8 | 9 | 10 | dbcparser_test: dbcmetaparser.o dbcparser.o ../src/file/dbcfile.h 11 | $(CXX) $(CXXFLAGS) dbcmetaparser.o dbcparser.o dbcparser_test.cpp $(LIBS) -o dbcparser_test 12 | @echo "Build finished" 13 | 14 | signalutil_test: signalutil.o 15 | $(CXX) $(CXXFLAGS) signalutil.o signalutil_test.cpp -o signalutil_test 16 | @echo "Build finished" 17 | 18 | dbcparser.o: ../src/file/dbcmetaparser.h ../src/file/dbcparser.cpp ../src/file/dbcparser.h 19 | $(CXX) -c $(CXXFLAGS) $(INC) ../src/file/dbcparser.cpp 20 | 21 | dbcmetaparser.o: ../src/file/dbcmetaparser.cpp ../src/file/dbcmetaparser.h 22 | $(CXX) -c $(CXXFLAGS) $(INC) ../src/file/dbcmetaparser.cpp 23 | 24 | signalutil.o: ../src/tincan/signalutil.cpp ../src/tincan/signalutil.h 25 | $(CXX) -c $(CXXFLAGS) ../src/tincan/signalutil.cpp 26 | -------------------------------------------------------------------------------- /test/signalutil_test.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../ext/doctest.h" 9 | #include "../src/tincan/signalutil.h" 10 | 11 | 12 | using doctest::Approx; 13 | 14 | 15 | TEST_CASE("pswap64") 16 | { 17 | // Bit positions of first byte after swapping an 8-byte block 18 | CHECK(tin::pswap64(0) == 56); 19 | CHECK(tin::pswap64(1) == 57); 20 | CHECK(tin::pswap64(2) == 58); 21 | CHECK(tin::pswap64(3) == 59); 22 | CHECK(tin::pswap64(4) == 60); 23 | CHECK(tin::pswap64(5) == 61); 24 | CHECK(tin::pswap64(6) == 62); 25 | CHECK(tin::pswap64(7) == 63); 26 | 27 | // Bit positions of second byte after swapping an 8-byte block 28 | CHECK(tin::pswap64(8) == 48); 29 | CHECK(tin::pswap64(9) == 49); 30 | CHECK(tin::pswap64(10) == 50); 31 | CHECK(tin::pswap64(11) == 51); 32 | CHECK(tin::pswap64(12) == 52); 33 | CHECK(tin::pswap64(13) == 53); 34 | CHECK(tin::pswap64(14) == 54); 35 | CHECK(tin::pswap64(15) == 55); 36 | 37 | // Bit positions of last byte after swapping an 8-byte block 38 | CHECK(tin::pswap64(56) == 0); 39 | CHECK(tin::pswap64(57) == 1); 40 | CHECK(tin::pswap64(58) == 2); 41 | CHECK(tin::pswap64(59) == 3); 42 | CHECK(tin::pswap64(60) == 4); 43 | CHECK(tin::pswap64(61) == 5); 44 | CHECK(tin::pswap64(62) == 6); 45 | CHECK(tin::pswap64(63) == 7); 46 | } 47 | 48 | 49 | TEST_CASE("build_raw_value") 50 | { 51 | std::array buffer{0}; 52 | buffer[1] = 0b1100'0000; 53 | buffer[2] = 0b1000'1110; 54 | buffer[3] = 0b0001'1011; 55 | 56 | SUBCASE("shifting") 57 | { 58 | auto order = tin::Byte_order::Intel; 59 | auto sign = tin::Value_sign::Unsigned; 60 | 61 | CHECK(std::get(tin::build_raw_value(buffer, 14, 15, order, sign)) == 28219); 62 | // Zero in the least significant bit 63 | CHECK(std::get(tin::build_raw_value(buffer, 13, 16, order, sign)) == 56438); 64 | // Zero in more significant bits 65 | CHECK(std::get(tin::build_raw_value(buffer, 14, 16, order, sign)) == 28219); 66 | // Single-byte 67 | CHECK(std::get(tin::build_raw_value(buffer, 0, 8, order, sign)) == 0); 68 | CHECK(std::get(tin::build_raw_value(buffer, 8, 6, order, sign)) == 0); 69 | CHECK(std::get(tin::build_raw_value(buffer, 8, 7, order, sign)) == 64); 70 | CHECK(std::get(tin::build_raw_value(buffer, 8, 8, order, sign)) == 192); 71 | CHECK(std::get(tin::build_raw_value(buffer, 10, 6, order, sign)) == 48); 72 | CHECK(std::get(tin::build_raw_value(buffer, 18, 6, order, sign)) == 35); 73 | CHECK(std::get(tin::build_raw_value(buffer, 24, 8, order, sign)) == 27); 74 | // Multi-byte 75 | CHECK(std::get(tin::build_raw_value(buffer, 0, 16, order, sign)) == 49152); 76 | CHECK(std::get(tin::build_raw_value(buffer, 0, 24, order, sign)) == 9355264); 77 | CHECK(std::get(tin::build_raw_value(buffer, 0, 32, order, sign)) == 462340096); 78 | CHECK(std::get(tin::build_raw_value(buffer, 16, 24, order, sign)) == 7054); 79 | CHECK(std::get(tin::build_raw_value(buffer, 19, 5, order, sign)) == 17); 80 | CHECK(std::get(tin::build_raw_value(buffer, 17, 8, order, sign)) == 199); 81 | CHECK(std::get(tin::build_raw_value(buffer, 22, 9, order, sign)) == 110); 82 | } 83 | 84 | SUBCASE("signed values") 85 | { 86 | auto order = tin::Byte_order::Intel; 87 | auto sign = tin::Value_sign::Signed; 88 | 89 | CHECK(std::get(tin::build_raw_value(buffer, 0, 8, order, sign)) == 0); 90 | CHECK(std::get(tin::build_raw_value(buffer, 8, 8, order, sign)) == -64); 91 | CHECK(std::get(tin::build_raw_value(buffer, 16, 8, order, sign)) == -114); 92 | CHECK(std::get(tin::build_raw_value(buffer, 17, 3, order, sign)) == -1); 93 | CHECK(std::get(tin::build_raw_value(buffer, 16, 4, order, sign)) == -2); 94 | CHECK(std::get(tin::build_raw_value(buffer, 0, 27, order, sign)) == 59686912); 95 | CHECK(std::get(tin::build_raw_value(buffer, 17, 4, order, sign)) == 7); 96 | CHECK(std::get(tin::build_raw_value(buffer, 28, 4, order, sign)) == 1); 97 | } 98 | 99 | SUBCASE("moto byte order") 100 | { 101 | auto order = tin::Byte_order::Moto; 102 | auto sign = tin::Value_sign::Unsigned; 103 | 104 | CHECK(std::get(tin::build_raw_value(buffer, 7, 8, order, sign)) == 0); 105 | CHECK(std::get(tin::build_raw_value(buffer, 15, 8, order, sign)) == 192); 106 | CHECK(std::get(tin::build_raw_value(buffer, 11, 9, order, sign)) == 17); 107 | CHECK(std::get(tin::build_raw_value(buffer, 14, 10, order, sign)) == 516); 108 | CHECK(std::get(tin::build_raw_value(buffer, 15, 22, order, sign)) == 3154822); 109 | CHECK(std::get(tin::build_raw_value(buffer, 17, 3, order, sign)) == 4); 110 | } 111 | } 112 | 113 | 114 | TEST_CASE("calc_phys_value") 115 | { 116 | CHECK(Approx(std::get(tin::calc_phys_value(237ull, 0.1, -10))) == 13.7); 117 | CHECK(Approx(std::get(tin::calc_phys_value(123456789ull, -0.1337, 0))) == -16506172.6893); 118 | CHECK(Approx(std::get(tin::calc_phys_value(1000ull, 0.0001, -0.1))) == 0); 119 | CHECK(Approx(std::get(tin::calc_phys_value(1001ull, 0.0001, -0.1))) == 0.0001); 120 | CHECK(Approx(std::get(tin::calc_phys_value(999ull, 0.0001, -0.1))) == -0.0001); 121 | CHECK(std::get(tin::calc_phys_value(237ull, 1, 0)) == 237); 122 | CHECK(std::get(tin::calc_phys_value(237ull, 10, -1000)) == 1370); 123 | CHECK(std::get(tin::calc_phys_value(123456789ull, 1000000000, 0)) == 123456789000000000); 124 | CHECK(std::get(tin::calc_phys_value(-1500ll, 1, 0)) == -1500); 125 | CHECK(std::get(tin::calc_phys_value(-1500ll, 1, 1500)) == 0); 126 | CHECK(std::get(tin::calc_phys_value(1500ll, 3, -3000)) == 1500); 127 | } 128 | -------------------------------------------------------------------------------- /test/test.dbc: -------------------------------------------------------------------------------- 1 | VERSION "" 2 | 3 | NS_: 4 | 5 | BS_: 6 | 7 | BU_: Vector__XXX VEHICLE HMI FLUX_CAPACITOR TIME_CIRCUITS RADAR_DEVICE 8 | 9 | BO_ 100 TEST_FRAME: 8 VEHICLE 10 | SG_ CRC : 0|8@1+ (1,0) [0|255] "" Vector__XXX 11 | SG_ ALIVE : 8|4@1+ (1,0) [0|15] "" Vector__XXX 12 | SG_ COUNTER : 12|12@1+ (1,0) [0|4095] "" Vector__XXX 13 | SG_ SINE : 24|11@1+ (0.001,-1.0) [-1.0|1.0] "" Vector__XXX 14 | SG_ LIGHT : 36|1@1+ (1,0) [0|1] "" Vector__XXX 15 | SG_ COLOR : 37|3@1+ (1,0) [0|15] "" Vector__XXX 16 | SG_ BRIGHTNESS : 40|7@1+ (1,0) [0|100] "%" Vector__XXX 17 | SG_ ALTITUDE : 48|16@1+ (0.001,0) [0|65535] "km" Vector__XXX 18 | 19 | BO_ 201 VEH_STATE: 8 VEHICLE 20 | SG_ CRC : 0|8@1+ (1,0) [0|255] "" Vector__XXX 21 | SG_ ALIVE : 8|4@1+ (1,0) [0|15] "" Vector__XXX 22 | SG_ VELOCITY : 12|14@1+ (0.01,0) [0|140] "mph" Vector__XXX 23 | SG_ WIPER_POSITION : 26|16@1+ (0.010986328125,0) [0|180] "deg" Vector__XXX 24 | 25 | BO_ 233 FLUX: 6 FLUX_CAPACITOR 26 | SG_ POWER_LEVEL : 0|32@1+ (0.000000001,0) [0|4.0] "GW" Vector__XXX 27 | SG_ DISPERSAL_RATE : 32|13@1+ (0.01220703125,0) [0|100] "%" Vector__XXX 28 | 29 | BO_ 245 DATE_TIME: 5 TIME_CIRCUITS 30 | SG_ TIME_TYPE : 0|2@1+ (1,0) [0|3] "" VEHICLE,FLUX_CAPACITOR 31 | SG_ YEAR : 2|14@1+ (1,0) [0|9999] "" VEHICLE,FLUX_CAPACITOR 32 | SG_ MONTH : 16|4@1+ (1,0) [1|12] "" VEHICLE,FLUX_CAPACITOR 33 | SG_ DAY : 20|5@1+ (1,0) [1|31] "" VEHICLE,FLUX_CAPACITOR 34 | SG_ AM_PM : 25|1@1+ (1,0) [0|1] "" VEHICLE,FLUX_CAPACITOR 35 | SG_ HOUR : 26|4@1+ (1,0) [0|12] "" VEHICLE,FLUX_CAPACITOR 36 | SG_ MINUTE : 30|6@1+ (1,0) [0|60] "" VEHICLE,FLUX_CAPACITOR 37 | 38 | BO_ 402 RADAR_OBJ_01: 4 RADAR_DEVICE 39 | SG_ OBJ_DIST_X : 9|14@0+ (0.01,0) [0|150] "m" Vector__XXX 40 | SG_ OBJ_DIST_Y : 7|14@0+ (0.01,0) [0|150] "m" Vector__XXX 41 | SG_ OBJ_COLOR : 27|4@0+ (1,0) [0|0] "" Vector__XXX 42 | 43 | BO_ 502 FUEL: 5 VEHICLE 44 | SG_ FUEL_TYPE M : 0|1@1+ (1,0) [0|0] "" HMI 45 | SG_ RESERVED : 1|7@1+ (1,0) [0|0] "" HMI 46 | SG_ E_RANGE m0 : 8|8@1+ (1,0) [0|250] "km" HMI 47 | SG_ GAS_RANGE m1 : 8|10@1+ (1,0) [0|1000] "km" HMI 48 | SG_ E_CONS m0 : 16|10@1+ (100,0) [0|100] "Wh/100km" HMI 49 | SG_ GAS_CONS m1 : 18|10@1+ (0.1,0) [0|100] "l/100km" HMI 50 | SG_ BAT_VOLT m0 : 26|12@1+ (0.1,0) [0|400] "V" HMI 51 | SG_ CHARGING m0 : 38|1@1+ (1,0) [0|0] "" HMI 52 | SG_ SUPER_CHARGING m0 : 39|1@1+ (1,0) [0|0] "" HMI 53 | 54 | VAL_ 100 LIGHT 0 "OFF" 1 "ON" ; 55 | VAL_ 100 COLOR 0 "UNDEFINED" 1 "RED" 2 "ORANGE" 3 "BLUE" 4 "YELLOW" 5 "GREEN" 6 "VIOLET" 7 "FUCHSIA" ; 56 | -------------------------------------------------------------------------------- /tincan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwkpe/tincan/7c47ea7fa7f43163f69c94f10dd39b56e4e95256/tincan.png -------------------------------------------------------------------------------- /tincan.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2017-09-12T10:46:53 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 9 | QT += network 10 | 11 | CONFIG += c++17 12 | CONFIG(release, debug|release) { 13 | CONFIG += optimize_full 14 | } 15 | 16 | TARGET = tincan 17 | TEMPLATE = app 18 | 19 | INCLUDEPATH += $$PWD/src \ 20 | $$PWD/ext \ 21 | C:/dev/boost_1_71_0 \ 22 | C:/dev/asio-1.12.2/include 23 | 24 | LIBS += -lstdc++fs \ 25 | -lws2_32 26 | 27 | DEFINES += ASIO_STANDALONE 28 | 29 | # The following define makes your compiler emit warnings if you use 30 | # any feature of Qt which as been marked as deprecated (the exact warnings 31 | # depend on your compiler). Please consult the documentation of the 32 | # deprecated API in order to know how to port your code away from it. 33 | DEFINES += QT_DEPRECATED_WARNINGS 34 | 35 | # You can also make your code fail to compile if you use deprecated APIs. 36 | # In order to do so, uncomment the following line. 37 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 38 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 39 | 40 | SOURCES += src/main.cpp \ 41 | src/models/cansignaldefitem.cpp \ 42 | src/models/cansignalitem.cpp \ 43 | src/models/valuedefitem.cpp \ 44 | src/models/valuedefmodel.cpp \ 45 | src/ui/mainwindow.cpp \ 46 | src/ui/treeviewdialog.cpp \ 47 | src/util.cpp \ 48 | src/file/dbcparser.cpp \ 49 | src/models/canbusmodel.cpp \ 50 | src/models/canframeitem.cpp \ 51 | src/models/treeitem.cpp \ 52 | src/models/treemodel.cpp \ 53 | src/tincan/canbus.cpp \ 54 | src/tincan/simulator.cpp \ 55 | src/tincan/translate.cpp \ 56 | ext/fmt/fmtlib.cc \ 57 | src/models/canbusdefmodel.cpp \ 58 | src/models/canframedefitem.cpp \ 59 | src/network/udpasyncreceiver.cpp \ 60 | src/tincan/signalutil.cpp \ 61 | src/tincan/cantracer.cpp \ 62 | src/network/canudpreceiver.cpp \ 63 | src/file/jsonreader.cpp \ 64 | src/file/jsonwriter.cpp \ 65 | src/file/dbcwriter.cpp \ 66 | src/file/dbcmetaparser.cpp 67 | 68 | HEADERS += src/ui/mainwindow.h \ 69 | src/models/cansignaldefitem.h \ 70 | src/models/cansignalitem.h \ 71 | src/models/treeitemid.h \ 72 | src/models/valuedefitem.h \ 73 | src/models/valuedefmodel.h \ 74 | src/network/dummyreceiver.h \ 75 | src/tincan/canrawframe.h \ 76 | src/tincan/cansignal.h \ 77 | src/tincan/cansignaldef.h \ 78 | src/ui/treeviewdialog.h \ 79 | src/util.h \ 80 | src/file/dbcfile.h \ 81 | src/file/dbcparser.h \ 82 | src/models/canbusmodel.h \ 83 | src/models/canframeitem.h \ 84 | src/models/treeitem.h \ 85 | src/models/treemodel.h \ 86 | src/tincan/canbus.h \ 87 | src/tincan/simulator.h \ 88 | src/tincan/canbusdef.h \ 89 | src/tincan/canframe.h \ 90 | src/tincan/canframedef.h \ 91 | src/tincan/translate.h \ 92 | ext/fmt/fmtlib.h \ 93 | src/models/canbusdefmodel.h \ 94 | src/models/canframedefitem.h \ 95 | src/tincan/errors.h \ 96 | src/network/udpasyncreceiver.h \ 97 | src/network/udpsocketguard.h \ 98 | src/tincan/signalutil.h \ 99 | src/tincan/cantracer.h \ 100 | src/network/canudpreceiver.h \ 101 | src/file/jsonreader.h \ 102 | src/file/jsonwriter.h \ 103 | src/file/dbcwriter.h \ 104 | src/file/dbcmetaparser.h 105 | 106 | FORMS += ui/mainwindow.ui \ 107 | ui/treeviewdialog.ui 108 | -------------------------------------------------------------------------------- /tincan_treeview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwkpe/tincan/7c47ea7fa7f43163f69c94f10dd39b56e4e95256/tincan_treeview.png -------------------------------------------------------------------------------- /ui/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1200 10 | 640 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 4 20 | 21 | 22 | 6 23 | 24 | 25 | 6 26 | 27 | 28 | 6 29 | 30 | 31 | 6 32 | 33 | 34 | 35 | 36 | 37 | 0 38 | 0 39 | 40 | 41 | 42 | QFrame::NoFrame 43 | 44 | 45 | Qt::Horizontal 46 | 47 | 48 | true 49 | 50 | 51 | 6 52 | 53 | 54 | false 55 | 56 | 57 | 58 | 59 | 0 60 | 0 61 | 62 | 63 | 64 | 2 65 | 66 | 67 | 68 | Connection 69 | 70 | 71 | 72 | 6 73 | 74 | 75 | 6 76 | 77 | 78 | 6 79 | 80 | 81 | 6 82 | 83 | 84 | 6 85 | 86 | 87 | 88 | 89 | UDP Gateway 90 | 91 | 92 | 93 | 94 | 95 | 4 96 | 97 | 98 | 0 99 | 100 | 101 | 0 102 | 103 | 104 | 0 105 | 106 | 107 | 0 108 | 109 | 110 | 111 | 112 | 113 | 48 114 | 0 115 | 116 | 117 | 118 | IP 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 160 127 | 0 128 | 129 | 130 | 131 | 132 | 160 133 | 16777215 134 | 135 | 136 | 137 | 0.0.0.0 138 | 139 | 140 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 141 | 142 | 143 | 144 | 145 | 146 | 147 | Qt::Horizontal 148 | 149 | 150 | 151 | 40 152 | 20 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 4 163 | 164 | 165 | 166 | 167 | 168 | 48 169 | 0 170 | 171 | 172 | 173 | Port 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 100 182 | 0 183 | 184 | 185 | 186 | 187 | 100 188 | 16777215 189 | 190 | 191 | 192 | 30001 193 | 194 | 195 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 196 | 197 | 198 | 199 | 200 | 201 | 202 | Qt::Horizontal 203 | 204 | 205 | 206 | 40 207 | 20 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | Open 220 | 221 | 222 | 223 | 224 | 225 | 226 | Qt::Horizontal 227 | 228 | 229 | 230 | 40 231 | 20 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | Simulator 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | Start 253 | 254 | 255 | 256 | 257 | 258 | 259 | Stop 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | Qt::Horizontal 269 | 270 | 271 | 272 | 40 273 | 20 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | Qt::Vertical 285 | 286 | 287 | 288 | 20 289 | 40 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | Bus definition 299 | 300 | 301 | 302 | 6 303 | 304 | 305 | 6 306 | 307 | 308 | 6 309 | 310 | 311 | 6 312 | 313 | 314 | 6 315 | 316 | 317 | 318 | 319 | 4 320 | 321 | 322 | 323 | 324 | Import 325 | 326 | 327 | 328 | 329 | 330 | 331 | true 332 | 333 | 334 | 335 | 336 | 337 | 338 | Clear 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | Qt::CustomContextMenu 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | Frame view 356 | 357 | 358 | 359 | 6 360 | 361 | 362 | 6 363 | 364 | 365 | 6 366 | 367 | 368 | 6 369 | 370 | 371 | 6 372 | 373 | 374 | 375 | 376 | 4 377 | 378 | 379 | 1 380 | 381 | 382 | 383 | 384 | Qt::Horizontal 385 | 386 | 387 | 388 | 40 389 | 20 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | Clear 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 0 408 | 0 409 | 410 | 411 | 412 | QFrame::StyledPanel 413 | 414 | 415 | false 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 0 425 | 426 | 427 | 428 | Trace 429 | 430 | 431 | 432 | 4 433 | 434 | 435 | 6 436 | 437 | 438 | 6 439 | 440 | 441 | 6 442 | 443 | 444 | 6 445 | 446 | 447 | 448 | 449 | 4 450 | 451 | 452 | 453 | 454 | Reset 455 | 456 | 457 | 458 | 459 | 460 | 461 | Clear 462 | 463 | 464 | 465 | 466 | 467 | 468 | Qt::Horizontal 469 | 470 | 471 | 472 | 40 473 | 20 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | Paused 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | Qt::NoContextMenu 491 | 492 | 493 | Qt::ScrollBarAlwaysOn 494 | 495 | 496 | false 497 | 498 | 499 | QPlainTextEdit::NoWrap 500 | 501 | 502 | true 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 0 520 | 0 521 | 1200 522 | 21 523 | 524 | 525 | 526 | 527 | 528 | Close 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | -------------------------------------------------------------------------------- /ui/treeviewdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tree_view_dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 500 10 | 300 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 0 19 | 20 | 21 | 0 22 | 23 | 24 | 0 25 | 26 | 27 | 0 28 | 29 | 30 | 0 31 | 32 | 33 | 34 | 35 | Qt::NoFocus 36 | 37 | 38 | Qt::NoContextMenu 39 | 40 | 41 | QFrame::NoFrame 42 | 43 | 44 | Qt::ScrollBarAlwaysOff 45 | 46 | 47 | Qt::ScrollBarAlwaysOff 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | --------------------------------------------------------------------------------