├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── screenshot_candumprb.png ├── screenshot_cangenrb.png └── src ├── bin ├── candumprb.rs ├── cangenrb.rs └── canstatsrb.rs ├── candump_parser.rs └── lib.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ansi_term" 7 | version = "0.11.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 10 | dependencies = [ 11 | "winapi", 12 | ] 13 | 14 | [[package]] 15 | name = "ansi_term" 16 | version = "0.12.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 19 | dependencies = [ 20 | "winapi", 21 | ] 22 | 23 | [[package]] 24 | name = "anyhow" 25 | version = "1.0.39" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "81cddc5f91628367664cc7c69714ff08deee8a3efc54623011c772544d7b2767" 28 | 29 | [[package]] 30 | name = "arrayvec" 31 | version = "0.5.2" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 34 | 35 | [[package]] 36 | name = "atty" 37 | version = "0.2.14" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 40 | dependencies = [ 41 | "hermit-abi", 42 | "libc", 43 | "winapi", 44 | ] 45 | 46 | [[package]] 47 | name = "autocfg" 48 | version = "1.0.1" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 51 | 52 | [[package]] 53 | name = "bitflags" 54 | version = "0.4.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3" 57 | 58 | [[package]] 59 | name = "bitflags" 60 | version = "1.2.1" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 63 | 64 | [[package]] 65 | name = "bytes" 66 | version = "1.0.1" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" 69 | 70 | [[package]] 71 | name = "can-dbc" 72 | version = "3.0.2" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "5bc2e1f9f6f7fe6f3dbcfc44036eb07596ef1d37517ce822111f0c3d6eeed82a" 75 | dependencies = [ 76 | "derive-getters", 77 | "nom 4.2.3", 78 | ] 79 | 80 | [[package]] 81 | name = "canutils" 82 | version = "1.0.0" 83 | dependencies = [ 84 | "ansi_term 0.12.1", 85 | "anyhow", 86 | "can-dbc", 87 | "futures", 88 | "nom 5.1.2", 89 | "pretty-hex", 90 | "rand", 91 | "rand_core", 92 | "serde", 93 | "serde_derive", 94 | "socketcan", 95 | "structopt", 96 | "tokio", 97 | "tokio-socketcan", 98 | ] 99 | 100 | [[package]] 101 | name = "cfg-if" 102 | version = "1.0.0" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 105 | 106 | [[package]] 107 | name = "clap" 108 | version = "2.33.3" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 111 | dependencies = [ 112 | "ansi_term 0.11.0", 113 | "atty", 114 | "bitflags 1.2.1", 115 | "strsim", 116 | "textwrap", 117 | "unicode-width", 118 | "vec_map", 119 | ] 120 | 121 | [[package]] 122 | name = "derive-getters" 123 | version = "0.1.1" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "16dc4e2517f08ca167440ccb11023c1308ee19a4022d7b03c0e652f971171869" 126 | dependencies = [ 127 | "proc-macro2", 128 | "quote", 129 | "syn", 130 | ] 131 | 132 | [[package]] 133 | name = "futures" 134 | version = "0.3.13" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1" 137 | dependencies = [ 138 | "futures-channel", 139 | "futures-core", 140 | "futures-executor", 141 | "futures-io", 142 | "futures-sink", 143 | "futures-task", 144 | "futures-util", 145 | ] 146 | 147 | [[package]] 148 | name = "futures-channel" 149 | version = "0.3.13" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" 152 | dependencies = [ 153 | "futures-core", 154 | "futures-sink", 155 | ] 156 | 157 | [[package]] 158 | name = "futures-core" 159 | version = "0.3.13" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" 162 | 163 | [[package]] 164 | name = "futures-executor" 165 | version = "0.3.13" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1" 168 | dependencies = [ 169 | "futures-core", 170 | "futures-task", 171 | "futures-util", 172 | ] 173 | 174 | [[package]] 175 | name = "futures-io" 176 | version = "0.3.13" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" 179 | 180 | [[package]] 181 | name = "futures-macro" 182 | version = "0.3.13" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7" 185 | dependencies = [ 186 | "proc-macro-hack", 187 | "proc-macro2", 188 | "quote", 189 | "syn", 190 | ] 191 | 192 | [[package]] 193 | name = "futures-sink" 194 | version = "0.3.13" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" 197 | 198 | [[package]] 199 | name = "futures-task" 200 | version = "0.3.13" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" 203 | 204 | [[package]] 205 | name = "futures-util" 206 | version = "0.3.13" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" 209 | dependencies = [ 210 | "futures-channel", 211 | "futures-core", 212 | "futures-io", 213 | "futures-macro", 214 | "futures-sink", 215 | "futures-task", 216 | "memchr", 217 | "pin-project-lite", 218 | "pin-utils", 219 | "proc-macro-hack", 220 | "proc-macro-nested", 221 | "slab", 222 | ] 223 | 224 | [[package]] 225 | name = "getrandom" 226 | version = "0.1.16" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 229 | dependencies = [ 230 | "cfg-if", 231 | "libc", 232 | "wasi", 233 | ] 234 | 235 | [[package]] 236 | name = "heck" 237 | version = "0.3.2" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" 240 | dependencies = [ 241 | "unicode-segmentation", 242 | ] 243 | 244 | [[package]] 245 | name = "hermit-abi" 246 | version = "0.1.18" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 249 | dependencies = [ 250 | "libc", 251 | ] 252 | 253 | [[package]] 254 | name = "hex" 255 | version = "0.2.0" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" 258 | 259 | [[package]] 260 | name = "instant" 261 | version = "0.1.9" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" 264 | dependencies = [ 265 | "cfg-if", 266 | ] 267 | 268 | [[package]] 269 | name = "itertools" 270 | version = "0.4.19" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "c4a9b56eb56058f43dc66e58f40a214b2ccbc9f3df51861b63d51dec7b65bc3f" 273 | 274 | [[package]] 275 | name = "lazy_static" 276 | version = "1.4.0" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 279 | 280 | [[package]] 281 | name = "lexical-core" 282 | version = "0.7.5" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374" 285 | dependencies = [ 286 | "arrayvec", 287 | "bitflags 1.2.1", 288 | "cfg-if", 289 | "ryu", 290 | "static_assertions", 291 | ] 292 | 293 | [[package]] 294 | name = "libc" 295 | version = "0.2.91" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" 298 | 299 | [[package]] 300 | name = "lock_api" 301 | version = "0.4.2" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" 304 | dependencies = [ 305 | "scopeguard", 306 | ] 307 | 308 | [[package]] 309 | name = "log" 310 | version = "0.4.14" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 313 | dependencies = [ 314 | "cfg-if", 315 | ] 316 | 317 | [[package]] 318 | name = "memchr" 319 | version = "2.3.4" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 322 | 323 | [[package]] 324 | name = "mio" 325 | version = "0.7.11" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" 328 | dependencies = [ 329 | "libc", 330 | "log", 331 | "miow", 332 | "ntapi", 333 | "winapi", 334 | ] 335 | 336 | [[package]] 337 | name = "miow" 338 | version = "0.3.7" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 341 | dependencies = [ 342 | "winapi", 343 | ] 344 | 345 | [[package]] 346 | name = "nix" 347 | version = "0.5.1" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "bfb3ddedaa14746434a02041940495bf11325c22f6d36125d3bdd56090d50a79" 350 | dependencies = [ 351 | "bitflags 0.4.0", 352 | "libc", 353 | ] 354 | 355 | [[package]] 356 | name = "nom" 357 | version = "4.2.3" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" 360 | dependencies = [ 361 | "memchr", 362 | "version_check 0.1.5", 363 | ] 364 | 365 | [[package]] 366 | name = "nom" 367 | version = "5.1.2" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" 370 | dependencies = [ 371 | "lexical-core", 372 | "memchr", 373 | "version_check 0.9.3", 374 | ] 375 | 376 | [[package]] 377 | name = "ntapi" 378 | version = "0.3.6" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 381 | dependencies = [ 382 | "winapi", 383 | ] 384 | 385 | [[package]] 386 | name = "num_cpus" 387 | version = "1.13.0" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 390 | dependencies = [ 391 | "hermit-abi", 392 | "libc", 393 | ] 394 | 395 | [[package]] 396 | name = "once_cell" 397 | version = "1.7.2" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" 400 | 401 | [[package]] 402 | name = "parking_lot" 403 | version = "0.11.1" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" 406 | dependencies = [ 407 | "instant", 408 | "lock_api", 409 | "parking_lot_core", 410 | ] 411 | 412 | [[package]] 413 | name = "parking_lot_core" 414 | version = "0.8.3" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" 417 | dependencies = [ 418 | "cfg-if", 419 | "instant", 420 | "libc", 421 | "redox_syscall", 422 | "smallvec", 423 | "winapi", 424 | ] 425 | 426 | [[package]] 427 | name = "pin-project-lite" 428 | version = "0.2.6" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" 431 | 432 | [[package]] 433 | name = "pin-utils" 434 | version = "0.1.0" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 437 | 438 | [[package]] 439 | name = "ppv-lite86" 440 | version = "0.2.10" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 443 | 444 | [[package]] 445 | name = "pretty-hex" 446 | version = "0.2.1" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131" 449 | 450 | [[package]] 451 | name = "proc-macro-error" 452 | version = "1.0.4" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 455 | dependencies = [ 456 | "proc-macro-error-attr", 457 | "proc-macro2", 458 | "quote", 459 | "syn", 460 | "version_check 0.9.3", 461 | ] 462 | 463 | [[package]] 464 | name = "proc-macro-error-attr" 465 | version = "1.0.4" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 468 | dependencies = [ 469 | "proc-macro2", 470 | "quote", 471 | "version_check 0.9.3", 472 | ] 473 | 474 | [[package]] 475 | name = "proc-macro-hack" 476 | version = "0.5.19" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 479 | 480 | [[package]] 481 | name = "proc-macro-nested" 482 | version = "0.1.7" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" 485 | 486 | [[package]] 487 | name = "proc-macro2" 488 | version = "1.0.24" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 491 | dependencies = [ 492 | "unicode-xid", 493 | ] 494 | 495 | [[package]] 496 | name = "quote" 497 | version = "1.0.9" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 500 | dependencies = [ 501 | "proc-macro2", 502 | ] 503 | 504 | [[package]] 505 | name = "rand" 506 | version = "0.7.3" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 509 | dependencies = [ 510 | "getrandom", 511 | "libc", 512 | "rand_chacha", 513 | "rand_core", 514 | "rand_hc", 515 | ] 516 | 517 | [[package]] 518 | name = "rand_chacha" 519 | version = "0.2.2" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 522 | dependencies = [ 523 | "ppv-lite86", 524 | "rand_core", 525 | ] 526 | 527 | [[package]] 528 | name = "rand_core" 529 | version = "0.5.1" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 532 | dependencies = [ 533 | "getrandom", 534 | ] 535 | 536 | [[package]] 537 | name = "rand_hc" 538 | version = "0.2.0" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 541 | dependencies = [ 542 | "rand_core", 543 | ] 544 | 545 | [[package]] 546 | name = "redox_syscall" 547 | version = "0.2.5" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" 550 | dependencies = [ 551 | "bitflags 1.2.1", 552 | ] 553 | 554 | [[package]] 555 | name = "ryu" 556 | version = "1.0.5" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 559 | 560 | [[package]] 561 | name = "scopeguard" 562 | version = "1.1.0" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 565 | 566 | [[package]] 567 | name = "serde" 568 | version = "1.0.125" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" 571 | 572 | [[package]] 573 | name = "serde_derive" 574 | version = "1.0.125" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" 577 | dependencies = [ 578 | "proc-macro2", 579 | "quote", 580 | "syn", 581 | ] 582 | 583 | [[package]] 584 | name = "signal-hook-registry" 585 | version = "1.3.0" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" 588 | dependencies = [ 589 | "libc", 590 | ] 591 | 592 | [[package]] 593 | name = "slab" 594 | version = "0.4.2" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 597 | 598 | [[package]] 599 | name = "smallvec" 600 | version = "1.6.1" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 603 | 604 | [[package]] 605 | name = "socketcan" 606 | version = "1.7.0" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "3101efc6ef5af6f1c1a488241b469757b7a183baca63af958cd90e4696446c80" 609 | dependencies = [ 610 | "hex", 611 | "itertools", 612 | "libc", 613 | "nix", 614 | "try_from", 615 | ] 616 | 617 | [[package]] 618 | name = "static_assertions" 619 | version = "1.1.0" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 622 | 623 | [[package]] 624 | name = "strsim" 625 | version = "0.8.0" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 628 | 629 | [[package]] 630 | name = "structopt" 631 | version = "0.3.21" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" 634 | dependencies = [ 635 | "clap", 636 | "lazy_static", 637 | "structopt-derive", 638 | ] 639 | 640 | [[package]] 641 | name = "structopt-derive" 642 | version = "0.4.14" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" 645 | dependencies = [ 646 | "heck", 647 | "proc-macro-error", 648 | "proc-macro2", 649 | "quote", 650 | "syn", 651 | ] 652 | 653 | [[package]] 654 | name = "syn" 655 | version = "1.0.64" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" 658 | dependencies = [ 659 | "proc-macro2", 660 | "quote", 661 | "unicode-xid", 662 | ] 663 | 664 | [[package]] 665 | name = "textwrap" 666 | version = "0.11.0" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 669 | dependencies = [ 670 | "unicode-width", 671 | ] 672 | 673 | [[package]] 674 | name = "thiserror" 675 | version = "1.0.24" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" 678 | dependencies = [ 679 | "thiserror-impl", 680 | ] 681 | 682 | [[package]] 683 | name = "thiserror-impl" 684 | version = "1.0.24" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" 687 | dependencies = [ 688 | "proc-macro2", 689 | "quote", 690 | "syn", 691 | ] 692 | 693 | [[package]] 694 | name = "tokio" 695 | version = "1.4.0" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722" 698 | dependencies = [ 699 | "autocfg", 700 | "bytes", 701 | "libc", 702 | "memchr", 703 | "mio", 704 | "num_cpus", 705 | "once_cell", 706 | "parking_lot", 707 | "pin-project-lite", 708 | "signal-hook-registry", 709 | "tokio-macros", 710 | "winapi", 711 | ] 712 | 713 | [[package]] 714 | name = "tokio-macros" 715 | version = "1.1.0" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" 718 | dependencies = [ 719 | "proc-macro2", 720 | "quote", 721 | "syn", 722 | ] 723 | 724 | [[package]] 725 | name = "tokio-socketcan" 726 | version = "0.3.0" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "6db5713701556875951c876d743de32b36ac8cba0c1cdbc6481b3ec3baf706be" 729 | dependencies = [ 730 | "futures", 731 | "libc", 732 | "mio", 733 | "socketcan", 734 | "thiserror", 735 | "tokio", 736 | ] 737 | 738 | [[package]] 739 | name = "try_from" 740 | version = "0.2.2" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "923a7ee3e97dbfe8685261beb4511cc9620a1252405d02693d43169729570111" 743 | 744 | [[package]] 745 | name = "unicode-segmentation" 746 | version = "1.7.1" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" 749 | 750 | [[package]] 751 | name = "unicode-width" 752 | version = "0.1.8" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 755 | 756 | [[package]] 757 | name = "unicode-xid" 758 | version = "0.2.1" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 761 | 762 | [[package]] 763 | name = "vec_map" 764 | version = "0.8.2" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 767 | 768 | [[package]] 769 | name = "version_check" 770 | version = "0.1.5" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" 773 | 774 | [[package]] 775 | name = "version_check" 776 | version = "0.9.3" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 779 | 780 | [[package]] 781 | name = "wasi" 782 | version = "0.9.0+wasi-snapshot-preview1" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 785 | 786 | [[package]] 787 | name = "winapi" 788 | version = "0.3.9" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 791 | dependencies = [ 792 | "winapi-i686-pc-windows-gnu", 793 | "winapi-x86_64-pc-windows-gnu", 794 | ] 795 | 796 | [[package]] 797 | name = "winapi-i686-pc-windows-gnu" 798 | version = "0.4.0" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 801 | 802 | [[package]] 803 | name = "winapi-x86_64-pc-windows-gnu" 804 | version = "0.4.0" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 807 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "canutils" 3 | version = "1.0.0" 4 | license = "MIT" 5 | description = "A can-utils implementation in pure Rust" 6 | authors = ["marcelbuesing "] 7 | edition = "2018" 8 | homepage = "https://github.com/marcelbuesing/canutils-rs" 9 | repository = "https://github.com/marcelbuesing/canutils-rs.git" 10 | keywords = ["can-utils", "can", "automotive", "ecu"] 11 | documentation = "https://docs.rs/canutils" 12 | readme = "README.md" 13 | 14 | [badges] 15 | travis-ci = { repository = "marcelbuesing/canutils-rs", branch = "master" } 16 | 17 | [dependencies] 18 | ansi_term = "0.12" 19 | anyhow = "1.0" 20 | can-dbc = "3.0" 21 | futures = "0.3" 22 | pretty-hex = "0.2" 23 | nom = "5.1" 24 | rand = "0.7" 25 | rand_core = "0.5" 26 | serde = { version = "1.0", optional = true } 27 | serde_derive = { version = "1.0", optional = true } 28 | structopt = "0.3" 29 | socketcan = "1.7" 30 | tokio = { version = "1.4", features = ["full"] } 31 | tokio-socketcan = "0.3" 32 | 33 | [[bin]] 34 | name = "candumprb" 35 | path = "src/bin/candumprb.rs" 36 | 37 | [[bin]] 38 | name = "cangenrb" 39 | 40 | [[bin]] 41 | name = "canstatsrb" 42 | 43 | [profile.release] 44 | opt-level = 3 45 | debug = false 46 | lto = true 47 | 48 | [features] 49 | with-serde = ["serde", "serde_derive"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![VERSION](https://img.shields.io/crates/v/canutils.svg)](https://crates.io/crates/canutils) 2 | [![Build Status](https://travis-ci.org/marcelbuesing/canutils-rs.svg?branch=master)](https://travis-ci.org/marcelbuesing/canutils-rs) 3 | [![docs](https://docs.rs/canutils/badge.svg)](https://docs.rs/canutils) 4 | 5 | # Binary Installation 6 | 7 | ``` 8 | cargo install --bin candumprb 9 | ``` 10 | 11 | # Candumprb 12 | 13 | A colorful candump version. 14 | 15 | ![screenshot](screenshot_candumprb.png) 16 | 17 | ## Interface 18 | 19 | ``` 20 | $ candumprb --help 21 | candumprb 1.0.0 22 | Candump Rainbow. A colorful can dump tool with dbc support. 23 | 24 | USAGE: 25 | candumprb [OPTIONS] 26 | 27 | FLAGS: 28 | -h, --help Prints help information 29 | -V, --version Prints version information 30 | 31 | OPTIONS: 32 | -i, --input DBC file path, if not passed frame signals are not decoded 33 | 34 | ARGS: 35 | socketcan CAN interface e.g. vcan0 36 | ``` 37 | 38 | # Cangenrb 39 | 40 | A colorful can frame generator that takes a DBC file as input for generating CAN frames. The random values of can signals are, by default, generated within the range specified in the DBC file. To create completely random frame payloads pass the --random-frame-data flag. 41 | 42 | ![screenshot](screenshot_cangenrb.png) 43 | 44 | ## Interface 45 | 46 | ``` 47 | $ cangenrb --help 48 | cangenrb 1.0.0 49 | Cangen Rainbow. A colorful that generates CAN messages based on a supplied DBC file. 50 | 51 | USAGE: 52 | cangenrb [FLAGS] [OPTIONS] --input 53 | 54 | FLAGS: 55 | --err Send random error frames 56 | -h, --help Prints help information 57 | -r, --random-frame-data Completely random frame data, unrelated to any signal 58 | --rtr Send random remote transmission frames 59 | -V, --version Prints version information 60 | 61 | OPTIONS: 62 | -f, --frequency Frequency of sending in microseconds [default: 100000] 63 | -i, --input DBC file path 64 | --transmitter Only generate messages of the given transmitter (sending node) 65 | 66 | ARGS: 67 | Only generate messages for the given receiver (receiving node) 68 | ``` 69 | # Canstatsrb 70 | 71 | A tool that collects statistics about received frames and the messages by message id. 72 | 73 | ## Interface 74 | ``` 75 | canstatsrb 1.0.0 76 | SocketCAN message statistics 77 | 78 | USAGE: 79 | canstatsrb 80 | 81 | FLAGS: 82 | -h, --help Prints help information 83 | -V, --version Prints version information 84 | 85 | ARGS: 86 | socketcan CAN interface e.g. vcan0 87 | ``` 88 | 89 | ## Output Example 90 | ``` 91 | RX Total: 6410 92 | EFF Total: 3697 ERR: 0 RTR: 0 93 | SFF Total: 2713 ERR: 0 RTR: 0 94 | Messages by CAN ID 95 | 32 → # 107 96 | 113 → # 93 97 | 152 → # 95 98 | 161 → # 99 99 | 163 → # 95 100 | 290 → # 115 101 | 341 → # 85 102 | 367 → # 83 103 | 383 → # 104 104 | 489 → # 92 105 | 504 → # 94 106 | 548 → # 107 107 | 549 → # 111 108 | 626 → # 88 109 | ``` -------------------------------------------------------------------------------- /screenshot_candumprb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcelbuesing/canutils-rs/9bac6898d0772ad7c940725d6eaa6ad8915e8044/screenshot_candumprb.png -------------------------------------------------------------------------------- /screenshot_cangenrb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcelbuesing/canutils-rs/9bac6898d0772ad7c940725d6eaa6ad8915e8044/screenshot_cangenrb.png -------------------------------------------------------------------------------- /src/bin/candumprb.rs: -------------------------------------------------------------------------------- 1 | use ansi_term::Color::{self, Cyan, Fixed, Green, Purple}; 2 | use anyhow::Result; 3 | use can_dbc::{ByteOrder, Signal}; 4 | use futures::StreamExt; 5 | use socketcan::CANFrame; 6 | use std::collections::HashMap; 7 | use std::convert::TryInto; 8 | use std::fmt::Write; 9 | use std::path::PathBuf; 10 | use structopt::StructOpt; 11 | use tokio::fs::File; 12 | use tokio::io::AsyncReadExt; 13 | use tokio_socketcan; 14 | 15 | const COLOR_CAN_ID: Color = Color::White; 16 | const COLOR_CAN_SFF: Color = Color::Blue; 17 | const COLOR_CAN_EFF: Color = Color::Red; 18 | 19 | const COLOR_NULL: Color = Fixed(242); // grey 20 | const COLOR_OFFSET: Color = Fixed(242); // grey 21 | const COLOR_ASCII_PRINTABLE: Color = Color::Cyan; 22 | const COLOR_ASCII_WHITESPACE: Color = Color::Green; 23 | const COLOR_ASCII_OTHER: Color = Color::Purple; 24 | const COLOR_NONASCII: Color = Color::Yellow; 25 | 26 | enum ByteCategory { 27 | Null, 28 | AsciiPrintable, 29 | AsciiWhitespace, 30 | AsciiOther, 31 | NonAscii, 32 | } 33 | 34 | #[derive(Copy, Clone)] 35 | struct Byte(u8); 36 | 37 | impl Byte { 38 | fn category(self) -> ByteCategory { 39 | if self.0 == 0x00 { 40 | ByteCategory::Null 41 | } else if self.0.is_ascii_alphanumeric() 42 | || self.0.is_ascii_punctuation() 43 | || self.0.is_ascii_graphic() 44 | { 45 | ByteCategory::AsciiPrintable 46 | } else if self.0.is_ascii_whitespace() { 47 | ByteCategory::AsciiWhitespace 48 | } else if self.0.is_ascii() { 49 | ByteCategory::AsciiOther 50 | } else { 51 | ByteCategory::NonAscii 52 | } 53 | } 54 | 55 | fn color(self) -> &'static Color { 56 | use ByteCategory::*; 57 | 58 | match self.category() { 59 | Null => &COLOR_NULL, 60 | AsciiPrintable => &COLOR_ASCII_PRINTABLE, 61 | AsciiWhitespace => &COLOR_ASCII_WHITESPACE, 62 | AsciiOther => &COLOR_ASCII_OTHER, 63 | NonAscii => &COLOR_NONASCII, 64 | } 65 | } 66 | 67 | fn as_char(self) -> char { 68 | use ByteCategory::*; 69 | 70 | match self.category() { 71 | Null => '0', 72 | AsciiPrintable => self.0 as char, 73 | AsciiWhitespace if self.0 == 0x20 => ' ', 74 | AsciiWhitespace => '_', 75 | AsciiOther => '•', 76 | NonAscii => '×', 77 | } 78 | } 79 | } 80 | 81 | #[derive(Debug, StructOpt)] 82 | #[structopt( 83 | name = "candumprb", 84 | about = "Candump Rainbow. A colorful can dump tool with dbc support." 85 | )] 86 | struct Opt { 87 | /// DBC file path, if not passed frame signals are not decoded 88 | #[structopt(short = "i", long = "input", parse(from_os_str))] 89 | input: Option, 90 | 91 | /// Set can interface 92 | #[structopt(help = "socketcan CAN interface e.g. vcan0")] 93 | can_interface: String, 94 | } 95 | 96 | #[tokio::main] 97 | async fn main() -> Result<()> { 98 | let opt = Opt::from_args(); 99 | 100 | let mut socket_rx = tokio_socketcan::CANSocket::open(&opt.can_interface).unwrap(); 101 | 102 | let byte_hex_table: Vec = (0u8..=u8::max_value()) 103 | .map(|i| { 104 | let byte_hex = format!("{:02x} ", i); 105 | Byte(i).color().paint(byte_hex).to_string() 106 | }) 107 | .collect(); 108 | 109 | // Read DBC and turn it into a hashmap for lookup 110 | let signal_lookup = if let Some(dbc_input) = opt.input.as_ref() { 111 | let mut f = File::open(dbc_input).await?; 112 | let mut buffer = Vec::new(); 113 | f.read_to_end(&mut buffer).await?; 114 | let dbc = can_dbc::DBC::from_slice(&buffer).expect("Failed to parse DBC"); 115 | 116 | let mut signal_lookup = HashMap::new(); 117 | 118 | for msg in dbc.messages() { 119 | signal_lookup.insert( 120 | msg.message_id().0 & !socketcan::EFF_FLAG, 121 | (msg.message_name().clone(), msg.signals().clone()), 122 | ); 123 | } 124 | Some(signal_lookup) 125 | } else { 126 | None 127 | }; 128 | 129 | while let Some(socket_result) = socket_rx.next().await { 130 | match socket_result { 131 | Ok(frame) => { 132 | if let Some(signal_lookup) = signal_lookup.as_ref() { 133 | print_dbc_signals(signal_lookup, &frame); 134 | } 135 | 136 | let mut buffer: String = String::new(); 137 | 138 | if frame.is_extended() { 139 | write!(buffer, "{}", COLOR_CAN_EFF.paint("EFF ")).unwrap(); 140 | } else { 141 | write!(buffer, "{}", COLOR_CAN_SFF.paint("SFF ")).unwrap(); 142 | } 143 | 144 | write!( 145 | buffer, 146 | "{}", 147 | COLOR_CAN_ID.paint(format!("{:08x} ", frame.id())) 148 | )?; 149 | 150 | for b in frame.data() { 151 | write!(buffer, "{}", byte_hex_table[*b as usize]).unwrap(); 152 | } 153 | 154 | println!("{}", buffer); 155 | } 156 | Err(err) => { 157 | eprintln!("IO error: {}", err); 158 | } 159 | } 160 | } 161 | 162 | Ok(()) 163 | } 164 | 165 | // Given a CAN Frame, lookup the can signals and print the signal values 166 | fn print_dbc_signals(signal_lookup: &HashMap)>, frame: &CANFrame) { 167 | let id = frame.id() & !socketcan::EFF_FLAG; 168 | let (message_name, signals) = signal_lookup.get(&id).expect("Unknown message id"); 169 | println!("\n{}", Purple.paint(message_name)); 170 | 171 | for signal in signals.iter() { 172 | let frame_data: [u8; 8] = frame 173 | .data() 174 | .try_into() 175 | .expect("slice with incorrect length"); 176 | 177 | let signal_value: u64 = if *signal.byte_order() == ByteOrder::LittleEndian { 178 | u64::from_le_bytes(frame_data) 179 | } else { 180 | u64::from_be_bytes(frame_data) 181 | }; 182 | 183 | // Calculate signal value 184 | let bit_mask: u64 = 2u64.pow(*signal.signal_size() as u32) - 1; 185 | let signal_value = ((signal_value >> signal.start_bit()) & bit_mask) as f32 186 | * *signal.factor() as f32 187 | + *signal.offset() as f32; 188 | 189 | let signal_value_s = format!("{:6.4}", signal_value); 190 | 191 | println!( 192 | "{} → value {}", 193 | Green.paint(signal.name()), 194 | Cyan.paint(signal_value_s) 195 | ); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/bin/cangenrb.rs: -------------------------------------------------------------------------------- 1 | use ansi_term::{ 2 | Color::Red, 3 | Colour::{Blue, Cyan, Green, Purple}, 4 | }; 5 | use can_dbc::{self, Message}; 6 | use pretty_hex::*; 7 | use rand::Rng; 8 | use socketcan; 9 | use std::{ 10 | fs::File, 11 | io::{self, prelude::*}, 12 | path::PathBuf, 13 | time::Instant, 14 | }; 15 | use structopt::StructOpt; 16 | use tokio::time; 17 | use tokio_socketcan::CANFrame; 18 | 19 | #[derive(Debug, StructOpt)] 20 | #[structopt( 21 | name = "cangenrb", 22 | about = "Cangen Rainbow. A colorful that generates CAN messages based on a supplied DBC file." 23 | )] 24 | struct Opt { 25 | /// Completely random frame data, unrelated to any signal 26 | #[structopt(short = "r", long = "random-frame-data")] 27 | random_frame_data: bool, 28 | 29 | /// DBC file path 30 | #[structopt(short = "i", long = "input", parse(from_os_str))] 31 | input: PathBuf, 32 | 33 | /// Send random remote transmission frames 34 | #[structopt(long = "rtr")] 35 | rtr_frames: bool, 36 | 37 | /// Send random error frames 38 | #[structopt(long = "err")] 39 | err_frames: bool, 40 | 41 | /// Frequency of sending in microseconds 42 | #[structopt(short = "f", long = "frequency", default_value = "100000")] 43 | frequency: u64, 44 | 45 | /// Only generate messages of the given transmitter (sending node) 46 | #[structopt(long = "transmitter")] 47 | transmitter: Option, 48 | 49 | /// Only generate messages for the given receiver (receiving node) 50 | //#[structopt(long = "receiver")] 51 | //receiver: Option, 52 | 53 | #[structopt(name = "CAN_INTERFACE")] 54 | can_interface: String, 55 | } 56 | 57 | #[tokio::main] 58 | async fn main() -> io::Result<()> { 59 | let opt = Opt::from_args(); 60 | 61 | let mut f = File::open(&opt.input)?; 62 | let mut buffer = Vec::new(); 63 | f.read_to_end(&mut buffer)?; 64 | 65 | let socket_tx = tokio_socketcan::CANSocket::open(&opt.can_interface).unwrap(); 66 | let dbc = can_dbc::DBC::from_slice(&buffer).expect("Failed to parse DBC"); 67 | 68 | // Filter messages by transmitter 69 | let dbc_messages = if let Some(transmitter) = opt.transmitter { 70 | let expected_transmitter = can_dbc::Transmitter::NodeName(transmitter); 71 | dbc.messages() 72 | .iter() 73 | .filter(|m| *m.transmitter() == expected_transmitter) 74 | .map(|m| m.to_owned()) 75 | .collect() 76 | } else { 77 | dbc.messages().clone() 78 | }; 79 | 80 | // Create signal generators 81 | let dbc_signal_range_gen = DBCSignalRangeGen { 82 | dbc_messages: dbc_messages.clone(), 83 | err_frames: opt.err_frames, 84 | rtr_frames: opt.rtr_frames, 85 | }; 86 | let random_frame_data_gen = RandomFrameDataGen { 87 | dbc_messages: dbc_messages.clone(), 88 | err_frames: opt.err_frames, 89 | rtr_frames: opt.rtr_frames, 90 | }; 91 | 92 | let mut messages_sent_counter: u128 = 0; 93 | 94 | let now = Instant::now(); 95 | let mut interval = time::interval(time::Duration::from_micros(opt.frequency)); 96 | loop { 97 | interval.tick().await; 98 | let can_frame = if opt.random_frame_data { 99 | random_frame_data_gen.gen() 100 | } else { 101 | dbc_signal_range_gen.gen() 102 | }; 103 | 104 | socket_tx.write_frame(can_frame).unwrap().await.unwrap(); 105 | 106 | messages_sent_counter += 1; 107 | 108 | let message_througput = messages_sent_counter as f64 / now.elapsed().as_secs() as f64; 109 | 110 | println!( 111 | "✉ #{} ✉ {:.2}msgs/s ⧖ {}ms", 112 | messages_sent_counter, 113 | message_througput, 114 | now.elapsed().as_millis() 115 | ); 116 | } 117 | } 118 | 119 | trait CanFrameGenStrategy: Send { 120 | fn gen(&self) -> CANFrame; 121 | } 122 | 123 | /// Generates signal values based on a given DBC messages. 124 | /// Signals offset and width is based on the DBC. 125 | /// Random signal values are generated within the range that is specified in the DBC. 126 | struct DBCSignalRangeGen { 127 | dbc_messages: Vec, 128 | rtr_frames: bool, 129 | err_frames: bool, 130 | } 131 | 132 | impl CanFrameGenStrategy for DBCSignalRangeGen { 133 | fn gen(&self) -> CANFrame { 134 | let mut rng = rand::thread_rng(); 135 | let message_idx: usize = rng.gen_range(0, self.dbc_messages.len() - 1); 136 | let message = self.dbc_messages.get(message_idx).unwrap(); 137 | 138 | println!("\n{}", Purple.paint(message.message_name())); 139 | let rtr_rand = if self.rtr_frames { 140 | rand::random() 141 | } else { 142 | false 143 | }; 144 | 145 | let err_rand = if self.err_frames { 146 | rand::random() 147 | } else { 148 | false 149 | }; 150 | 151 | let rand_frame_data = if *message.message_size() > 8 { 152 | println!("Non random message body due to currently unsupported size `{}` - id: `{:x}`. Size {} > 8", message.message_name(), message.message_id().0, message.message_size()); 153 | [0; 8] 154 | } else { 155 | self.gen_msg_frame_data(&message) 156 | }; 157 | 158 | println!( 159 | "→ ERR: {} RTR: {} Data: {}", 160 | err_rand, 161 | rtr_rand, 162 | rand_frame_data.to_vec().hex_dump() 163 | ); 164 | 165 | let message_id = message.message_id().0 & socketcan::EFF_MASK; 166 | 167 | CANFrame::new(message_id, &rand_frame_data, rtr_rand, err_rand) 168 | .expect("Failed to create frame") 169 | } 170 | } 171 | 172 | impl DBCSignalRangeGen { 173 | fn gen_msg_frame_data(&self, message: &can_dbc::Message) -> [u8; 8] { 174 | let mut frame_data_rand: u64 = 0; 175 | 176 | let mut rng = rand::thread_rng(); 177 | 178 | for signal in message.signals() { 179 | let actual_value: f64 = if signal.min() == signal.max() { 180 | println!( 181 | "Min and max value `{} = {}` match for signal {}, can not create random value.", 182 | signal.min(), 183 | signal.max(), 184 | signal.name() 185 | ); 186 | *signal.min() 187 | } else { 188 | rng.gen_range(signal.min(), signal.max()) 189 | }; 190 | 191 | let random_signal_value = (actual_value - signal.offset) / signal.factor; 192 | let bit_mask: u64 = 2u64.pow(*signal.signal_size() as u32) - 1; 193 | 194 | let min_s = format!("{}", signal.min()); 195 | let max_s = format!("{}", signal.max()); 196 | // let actual_value_s = format!("{:6.4}", (actual_value as u64 & bit_mask) as f64); 197 | let actual_value_s = format!("{:6.4}", actual_value); 198 | 199 | println!( 200 | "{:10.10} min {:6.4} max {:6.4} → value {}", 201 | Green.paint(signal.name()), 202 | Blue.paint(min_s), 203 | Red.paint(max_s), 204 | Cyan.paint(actual_value_s), 205 | ); 206 | 207 | assert!(actual_value >= *signal.min()); 208 | assert!(actual_value <= *signal.max()); 209 | 210 | let shifted_signal_value = 211 | (random_signal_value as u64 & bit_mask) << (signal.start_bit as u8); 212 | 213 | frame_data_rand |= shifted_signal_value; 214 | } 215 | 216 | frame_data_rand.to_le_bytes() 217 | } 218 | } 219 | 220 | /// Generate random frame payloads. The generated payload does not conform to the DBC. 221 | /// DBC message ids are based on the given DBC. 222 | struct RandomFrameDataGen { 223 | dbc_messages: Vec, 224 | rtr_frames: bool, 225 | err_frames: bool, 226 | } 227 | 228 | impl CanFrameGenStrategy for RandomFrameDataGen { 229 | fn gen(&self) -> CANFrame { 230 | let mut rng = rand::thread_rng(); 231 | let message_idx: usize = rng.gen_range(0, self.dbc_messages.len() - 1); 232 | let message = self.dbc_messages.get(message_idx).unwrap(); 233 | 234 | println!("\n{}", Purple.paint(message.message_name())); 235 | let rtr_rand = if self.rtr_frames { 236 | rand::random() 237 | } else { 238 | false 239 | }; 240 | 241 | let err_rand = if self.err_frames { 242 | rand::random() 243 | } else { 244 | false 245 | }; 246 | 247 | let mut rand_frame_data: [u8; 8] = [0; 8]; 248 | rng.fill(&mut rand_frame_data[..]); 249 | 250 | println!( 251 | "→ ERR: {} RTR: {} Data: {}", 252 | err_rand, 253 | rtr_rand, 254 | rand_frame_data.to_vec().hex_dump() 255 | ); 256 | 257 | let message_id = message.message_id().0 & socketcan::EFF_MASK; 258 | CANFrame::new(message_id, &rand_frame_data, rtr_rand, err_rand) 259 | .expect("Failed to create frame") 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/bin/canstatsrb.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Display; 2 | use futures::StreamExt; 3 | use std::{collections::HashMap, fmt}; 4 | use structopt::StructOpt; 5 | use tokio_socketcan; 6 | 7 | #[derive(Debug, StructOpt)] 8 | #[structopt(name = "canstatsrb", about = "SocketCAN message statistics")] 9 | struct Opt { 10 | /// Set can interface 11 | #[structopt(help = "socketcan CAN interface e.g. vcan0")] 12 | can_interface: String, 13 | } 14 | 15 | #[derive(Default, Debug)] 16 | struct Stats { 17 | msg_ids: HashMap, 18 | rx_frames: u64, 19 | eff_frames_total: u64, 20 | eff_frames_err: u64, 21 | eff_frames_rtr: u64, 22 | sff_frames_total: u64, 23 | sff_frames_err: u64, 24 | sff_frames_rtr: u64, 25 | } 26 | 27 | impl Display for Stats { 28 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 29 | writeln!(f, "RX Total: {}", self.rx_frames)?; 30 | 31 | write!(f, "EFF Total: {}\t", self.eff_frames_total)?; 32 | write!(f, "ERR: {}\t", self.eff_frames_err)?; 33 | writeln!(f, "RTR: {}", self.eff_frames_rtr)?; 34 | 35 | write!(f, "SFF Total: {}\t", self.sff_frames_total)?; 36 | write!(f, "ERR: {}\t", self.sff_frames_err)?; 37 | writeln!(f, "RTR: {}", self.sff_frames_rtr)?; 38 | writeln!(f, "Messages by CAN ID")?; 39 | 40 | let mut count_vec: Vec<(&u32, &u64)> = self.msg_ids.iter().collect(); 41 | count_vec.sort_by(|a, b| a.0.cmp(b.0)); 42 | for (ref id, ref count) in &count_vec { 43 | writeln!(f, "{: ^10} → #{: ^7}", id, count)?; 44 | } 45 | Ok(()) 46 | } 47 | } 48 | 49 | #[tokio::main] 50 | async fn main() -> std::io::Result<()> { 51 | let opt = Opt::from_args(); 52 | 53 | let mut socket_rx = tokio_socketcan::CANSocket::open(&opt.can_interface).unwrap(); 54 | 55 | let mut stats: Stats = Default::default(); 56 | 57 | while let Some(socket_result) = socket_rx.next().await { 58 | match socket_result { 59 | Ok(frame) => { 60 | stats.rx_frames += 1; 61 | 62 | if frame.is_extended() { 63 | stats.sff_frames_total += 1; 64 | 65 | if frame.is_error() { 66 | stats.sff_frames_err += 1; 67 | } 68 | 69 | if frame.is_rtr() { 70 | stats.sff_frames_rtr += 1; 71 | } 72 | } else { 73 | stats.eff_frames_total += 1; 74 | 75 | if frame.is_error() { 76 | stats.eff_frames_err += 1; 77 | } 78 | 79 | if frame.is_rtr() { 80 | stats.eff_frames_rtr += 1; 81 | } 82 | } 83 | 84 | stats 85 | .msg_ids 86 | .entry(frame.id()) 87 | .and_modify(|e| *e += 1) 88 | .or_insert(1); 89 | 90 | println!("{}", stats); 91 | } 92 | Err(err) => { 93 | eprintln!("IO error: {}", err); 94 | } 95 | } 96 | } 97 | 98 | Ok(()) 99 | } 100 | -------------------------------------------------------------------------------- /src/candump_parser.rs: -------------------------------------------------------------------------------- 1 | use nom::character::complete::{alphanumeric1, digit1, hex_digit1, space0}; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use crate::candump_parser::*; 6 | 7 | #[test] 8 | fn it_works() { 9 | let exp = DumpEntry { 10 | timestamp: Timestamp { 11 | seconds: 1547046014, 12 | nanos: 597158, 13 | }, 14 | can_interface: "vcan0".to_string(), 15 | can_frame: CanFrame { 16 | frame_id: 123, 17 | frame_body: 455, 18 | }, 19 | }; 20 | assert_eq!( 21 | dump_entry("(1547046014.597158) vcan0 7B#1C7"), 22 | Ok(("", exp)) 23 | ); 24 | } 25 | } 26 | 27 | #[derive(Debug, PartialEq)] 28 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 29 | pub struct Timestamp { 30 | pub seconds: u64, 31 | pub nanos: u64, 32 | } 33 | 34 | named!(timestamp<&str, Timestamp>, 35 | do_parse!( 36 | tag!("(") >> 37 | seconds: map_res!(digit1, |d: &str| d.parse()) >> 38 | tag!(".") >> 39 | nanos: map_res!(digit1, |d: &str| d.parse()) >> 40 | tag!(")") >> 41 | (Timestamp { seconds, nanos }) 42 | ) 43 | ); 44 | 45 | #[derive(Debug, PartialEq)] 46 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 47 | pub struct CanFrame { 48 | pub frame_id: u32, 49 | pub frame_body: u64, 50 | } 51 | 52 | named!(can_frame<&str, CanFrame>, 53 | do_parse!( 54 | frame_id: map_res!(hex_digit1, |d| u32::from_str_radix(d, 16)) >> 55 | tag!("#") >> 56 | frame_body: map_res!(hex_digit1, |d| u64::from_str_radix(d, 16)) >> 57 | (CanFrame { frame_id, frame_body }) 58 | ) 59 | ); 60 | 61 | #[derive(Debug, PartialEq)] 62 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 63 | pub struct DumpEntry { 64 | timestamp: Timestamp, 65 | can_interface: String, 66 | can_frame: CanFrame, 67 | } 68 | 69 | impl DumpEntry { 70 | pub fn timestamp(&self) -> &Timestamp { 71 | &self.timestamp 72 | } 73 | 74 | pub fn can_interface(&self) -> &str { 75 | &self.can_interface 76 | } 77 | 78 | pub fn can_frame(&self) -> &CanFrame { 79 | &self.can_frame 80 | } 81 | } 82 | 83 | named!(pub dump_entry<&str, DumpEntry>, 84 | do_parse!( 85 | timestamp: timestamp >> 86 | space0 >> 87 | can_interface: alphanumeric1 >> 88 | space0 >> 89 | can_frame: can_frame >> 90 | (DumpEntry { timestamp, can_interface: can_interface.to_string(), can_frame }) 91 | ) 92 | ); 93 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate nom; 3 | 4 | pub mod candump_parser; 5 | --------------------------------------------------------------------------------