diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml new file mode 100644 index 0000000..c1f2f67 --- /dev/null +++ b/.github/workflows/rust-ci.yml @@ -0,0 +1,27 @@ +name: Rust CI + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Setup Rust + uses: actions/checkout@v2 + - name: Install cargo-audit + run: cargo install cargo-audit + - name: Build + run: cargo build --verbose + - name: Test + run: cargo test --verbose + - name: Clippy + run: cargo clippy --verbose -- -D warnings + - name: Audit + run: cargo audit diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e397228 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "editor.defaultFormatter": "rust-lang.rust-analyzer", + "rust-analyzer.linkedProjects": [ + "./Cargo.toml" + ] +} diff --git a/Cargo.lock b/Cargo.lock index 19d32cf..2d1a1ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -20,6 +26,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -34,27 +51,34 @@ checksum = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3" [[package]] name = "bumpalo" -version = "3.12.1" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytemuck" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" [[package]] -name = "bytesize" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38fcc2979eff34a4b84e1cf9a1e3da42a7d44b3b690a40cdcb23e3d556cfb2e5" +name = "calypso" +version = "0.1.0" +dependencies = [ + "chrono", + "nalgebra", + "paho-mqtt", + "socketcan", +] [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -64,34 +88,41 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", - "time 0.1.45", "wasm-bindgen", - "winapi", + "windows-targets", +] + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", ] [[package]] -name = "codespan-reporting" -version = "0.11.1" +name = "concurrent-queue" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" dependencies = [ - "termcolor", - "unicode-width", + "crossbeam-utils", ] [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "crossbeam-channel" @@ -104,95 +135,113 @@ dependencies = [ ] [[package]] -name = "crossbeam-deque" -version = "0.8.3" +name = "crossbeam-utils" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", ] [[package]] -name = "crossbeam-epoch" -version = "0.9.14" +name = "event-listener" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] -name = "crossbeam-utils" -version = "0.8.15" +name = "futures" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ - "cfg-if", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] -name = "cxx" -version = "1.0.94" +name = "futures-channel" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", + "futures-core", + "futures-sink", ] [[package]] -name = "cxx-build" -version = "1.0.94" +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.15", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "cxxbridge-flags" -version = "1.0.94" +name = "futures-io" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] -name = "cxxbridge-macro" -version = "1.0.94" +name = "futures-macro" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] -name = "either" -version = "1.8.1" +name = "futures-sink" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] -name = "hermit-abi" -version = "0.2.6" +name = "futures-task" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ - "libc", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", ] [[package]] @@ -203,26 +252,25 @@ checksum = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -233,78 +281,46 @@ checksum = "c4a9b56eb56058f43dc66e58f40a214b2ccbc9f3df51861b63d51dec7b65bc3f" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" -version = "0.2.142" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" - -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "matrixmultiply" -version = "0.3.3" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb99c395ae250e1bf9133673f03ca9f97b7e71b705436bf8f089453445d1e9fe" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" dependencies = [ + "autocfg", "rawpointer", ] [[package]] name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "nalgebra" -version = "0.32.2" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68d47bba83f9e2006d117a9a33af1524e655516b8919caac694427a6fb1e511" +checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" dependencies = [ "approx", "matrixmultiply", @@ -318,28 +334,15 @@ dependencies = [ [[package]] name = "nalgebra-macros" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" +checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] -[[package]] -name = "ner_processing" -version = "0.1.0" -dependencies = [ - "chrono", - "matrixmultiply", - "nalgebra", - "rayon", - "socketcan", - "systemstat", - "transpose", -] - [[package]] name = "nix" version = "0.5.1" @@ -350,21 +353,11 @@ dependencies = [ "libc", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "num-complex" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" dependencies = [ "num-traits", ] @@ -392,107 +385,113 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] -name = "paste" -version = "1.0.12" +name = "openssl-sys" +version = "0.9.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] [[package]] -name = "proc-macro2" -version = "1.0.56" +name = "paho-mqtt" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "19e405de34b835fb6457d8b0169eda21949f855472b3e346556af9e29fac6eb2" dependencies = [ - "unicode-ident", + "async-channel", + "crossbeam-channel", + "futures", + "futures-timer", + "libc", + "log", + "paho-mqtt-sys", + "thiserror", ] [[package]] -name = "quote" -version = "1.0.26" +name = "paho-mqtt-sys" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5e482419d847af4ec43c07eed70f5f94f87dc712d267aecc91ab940944ab6bf4" dependencies = [ - "proc-macro2", + "cmake", + "openssl-sys", ] [[package]] -name = "rawpointer" -version = "0.2.1" +name = "paste" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] -name = "rayon" -version = "1.7.0" +name = "pin-project-lite" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] -name = "rayon-core" -version = "1.11.0" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "safe_arch" -version = "0.6.0" +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro2" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ - "bytemuck", + "unicode-ident", ] [[package]] -name = "scopeguard" -version = "1.1.0" +name = "quote" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] [[package]] -name = "scratch" -version = "1.0.5" +name = "rawpointer" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] -name = "serde" -version = "1.0.160" +name = "safe_arch" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +dependencies = [ + "bytemuck", +] [[package]] name = "simba" @@ -507,6 +506,15 @@ dependencies = [ "wide", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "socketcan" version = "1.7.0" @@ -520,12 +528,6 @@ dependencies = [ "try_from", ] -[[package]] -name = "strength_reduce" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" - [[package]] name = "syn" version = "1.0.109" @@ -539,9 +541,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -549,63 +551,23 @@ dependencies = [ ] [[package]] -name = "systemstat" -version = "0.2.3" +name = "thiserror" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a24aec24a9312c83999a28e3ef9db7e2afd5c64bf47725b758cdc1cafd5b0bd2" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ - "bytesize", - "lazy_static", - "libc", - "nom", - "time 0.3.20", - "winapi", + "thiserror-impl", ] [[package]] -name = "termcolor" -version = "1.2.0" +name = "thiserror-impl" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ - "winapi-util", -] - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" -dependencies = [ - "serde", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - -[[package]] -name = "transpose" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6522d49d03727ffb138ae4cbc1283d3774f0d10aa7f9bf52e6784c45daf9b23" -dependencies = [ - "num-integer", - "strength_reduce", + "proc-macro2", + "quote", + "syn 2.0.39", ] [[package]] @@ -616,33 +578,27 @@ checksum = "923a7ee3e97dbfe8685261beb4511cc9620a1252405d02693d43169729570111" [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "unicode-width" -version = "0.1.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -650,24 +606,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.39", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -675,78 +631,47 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "wide" -version = "0.7.8" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b689b6c49d6549434bf944e6b0f39238cf63693cb7a147e9d887507fffa3b223" +checksum = "c68938b57b33da363195412cfc5fc37c9ed49aa9cfe2156fde64b8d2c9498242" dependencies = [ "bytemuck", "safe_arch", ] [[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -759,42 +684,42 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml index 36b9d1b..15c0807 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,12 @@ [package] -name = "ner_processing" +name = "calypso" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] - -rayon = "1.5.1" -chrono = "0.4.19" -systemstat = "0.2.1" +chrono = "0.4.31" socketcan = "1.7.0" -transpose = "0.2.2" -matrixmultiply = "0.3.3" -nalgebra = "0.32.2" +paho-mqtt = "0.12.3" +nalgebra = "0.32.3" diff --git a/README.md b/README.md index 2e85bac..17995cc 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,34 @@ # Calypso Custom CAN Decoder for all the data being streamed around the car -### NERO Config +### Recommended Extensions +View https://www.youtube.com/watch?v=BU1LYFkpJuk for more information + +- rust-analyzer +- CodeLLDB +- Even Better TOML +- Error Lens +- Todo Tree +- crates + +#### Go to Settings in VSCode +search Rust-analyzer check and set the command from check -> clippy + +#### Open Settings.json +add following information: +``` +"[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer", + "editor.formatOnSave": true +} +``` + +### NERO 1.0 Config Utilizes a linux IPC to stream data to the NERO frontend -### SIREN Config +run ```/home/ner/Desktop/Calypso/target/release/calypso ipc /tmp/ipc.sock``` + +### SIREN and NERO 2.0 Config Utilizes MQTT Web Socket to offload data from the car for our telemetry system +run ```/home/ner/Desktop/Calypso/target/release/calypso mqtt localhost:1883``` + diff --git a/oxy/RustSynth.py b/oxy/RustSynth.py index c332f4c..2d3c491 100644 --- a/oxy/RustSynth.py +++ b/oxy/RustSynth.py @@ -1,42 +1,101 @@ from structs.CANField import CANField from structs.CANMsg import CANMsg -from structs.Decoding import Decoding - -from typing import Optional +from structs.Messages import Messages +from structs.Result import Result class RustSynth: ''' A class to synthesize Rust from a given CANMsg spec. ''' - inst_hashmap: str = " let mut result = HashMap::new();" - closing: str = " result\n}" + ignore_clippy: str = "#![allow(clippy::all)]\n" + decode_data_import: str = "use super::data::{Data,FormatData as fd, ProcessData as pd}; \n" + + decode_return_type: str = "Vec::" + decode_return_value: str = f" let result = vec![" + decode_close: str = " ]; \n result\n}\n" + + decode_mock: str = """ +pub fn decode_mock(_data: &[u8]) -> Vec:: { + let result = vec![ + Data::new(0.0, "Mock", "") + ]; + result +} +""" + + master_mapping_import: str = "use super::decode_data::*; \nuse super::data::Data; \n" + + master_mapping_signature: str = "pub fn get_message_info(id: &u32) -> MessageInfo { \n match id {" + + master_mapping_closing: str = " _ => MessageInfo::new(decode_mock), \n }\n}" + + message_info = """ +pub struct MessageInfo { + pub decoder: fn(data: &[u8]) -> Vec, +} + +impl MessageInfo { + pub fn new(decoder: fn(data: &[u8]) -> Vec) -> Self { + Self { + decoder + } + } +} +""" + + def parse_messages(self, msgs: [CANMsg]) -> Result: + result = Result("", "") + result.decode_data += self.ignore_clippy + result.decode_data += self.decode_data_import + result.decode_data += self.decode_mock + + result.master_mapping += self.master_mapping_import + result.master_mapping += self.message_info + result.master_mapping += self.master_mapping_signature + + for msg in msgs: + result.decode_data += self.synthesize(msg) + "\n" + result.master_mapping += self.map_msg_to_decoder(msg) + + result.master_mapping += self.master_mapping_closing + return result + + def map_msg_to_decoder(self, msg: CANMsg) -> str: + return f" {msg.id} => MessageInfo::new({self.function_name(msg.desc)}),\n" def synthesize(self, msg: CANMsg) -> str: signature: str = self.signature(msg.desc) generated_lines: list[str] = [] for field in msg.fields: - generated_lines.append(self.finalize_line(field.id, f"({self.parse_decoders(field)}){self.correcting_factor(field)}")) - total_list: list[str] = [signature, self.inst_hashmap] + generated_lines + [self.closing] + generated_lines.append(self.finalize_line(field.name, field.unit, f"{self.format_data(field, self.parse_decoders(field))}")) + total_list: list[str] = [signature, self.decode_return_value] + generated_lines + [self.decode_close] return "\n".join(total_list) + + def function_name(self, desc: str) -> str: + return f"decode_{desc.replace(' ', '_').lower()}" - def signature(self, to_decode: str) -> str: - return f"pub fn decode_{to_decode.replace(' ', '_')}(data: &[u8]) -> HashMap {{" + def signature(self, desc: str) -> str: + return f"pub fn {self.function_name(desc)}(data: &[u8]) -> {self.decode_return_type} {{" - def finalize_line(self, id: int, val: str) -> str: - return f" result.insert({id}, {val});" + def finalize_line(self, topic: str, unit: str, val: str) -> str: + return f" Data::new({val}, \"{topic}\", \"{unit}\")," def parse_decoders(self, field: CANField) -> str: if isinstance(field.decodings, type(None)): - return f"data[{field.index}] as f32" + return f"data[{field.index}] as {field.final_type}" else: - base: str = f"&data[{field.index}..{field.index + field.size}]" + base: str + if field.size == 1: + base = f"data[{field.index}]" + else : + base = f"&data[{field.index}..{field.index + field.size}]" for decoder in field.decodings: - base = f"pd::{decoder.repr}({base}, {decoder.bits}) as {decoder.final_type}" - return base + base = f"pd::{decoder.repr}({base} as {decoder.entry_type}, {decoder.bits})" + return f"{base} as {field.final_type}" - def correcting_factor(self, field:CANField) -> str: - cf: str = "" - if field.correcting_factor: - cf = f" {field.correcting_factor.op} {field.correcting_factor.const}" + def format_data(self, field:CANField, decoded_data: str) -> str: + cf = decoded_data + if field.format: + cf = f"fd::{field.format}({decoded_data})" return cf diff --git a/oxy/YAMLParser.py b/oxy/YAMLParser.py index 72a935f..8805bb4 100644 --- a/oxy/YAMLParser.py +++ b/oxy/YAMLParser.py @@ -1,10 +1,10 @@ -from io import TextIOWrapper from ruamel.yaml import YAML, Any from structs.CANMsg import CANMsg from structs.CANField import CANField -from structs.CorrectingFactor import CorrectingFactor -import structs.Decoding +from structs.Format import Format +from structs.Decoding import Decoding +from structs.Messages import Messages class YAMLParser: ''' @@ -14,12 +14,12 @@ class YAMLParser: def __init__(self): self.yaml = YAML() + self.yaml.register_class(Messages) self.yaml.register_class(CANMsg) self.yaml.register_class(CANField) - self.yaml.register_class(CorrectingFactor) - for decoding in structs.Decoding.Decoding.__subclasses__(): + for decoding in Decoding.__subclasses__(): self.yaml.register_class(decoding) - def parse(self, file: Any) -> CANMsg: + def parse(self, file: Any) -> Messages: return self.yaml.load(file) diff --git a/oxy/mapping.yaml b/oxy/mapping.yaml deleted file mode 100644 index 7182e03..0000000 --- a/oxy/mapping.yaml +++ /dev/null @@ -1,54 +0,0 @@ -!CANMsg -id: 1 -desc: "accumulator status" -fields: -- !CANField - id: 1 - name: Pack Inst Voltage - units: "V" - size: 2 - decodings: - - !BigEndian - bits: 8 - final_type: "f32" - correcting_factor: - !CorrectingFactor - const: 10.0 - op: "/" -- !CANField - id: 2 - name: "Pack Current" - units: "A" - size: 2 - decodings: - - !BigEndian - bits: 8 - final_type: "u32" - - !TwosComplement - bits: 16 - final_type: "f32" - correcting_factor: - !CorrectingFactor - const: 10.0 - op: "/" -- !CANField - id: 3 - name: "Pack Amp-hours" - units: "Ah" - size: 2 - decodings: - - !BigEndian - bits: 8 - final_type: "f32" -- !CANField - id: 4 - name: "Pack SOC" - units: "%" - size: 1 - final_type: "f32" -- !CANField - id: 5 - name: "Pack Health" - units: "%" - size: 1 - final_type: "f32" diff --git a/oxy/poc_translator.py b/oxy/poc_translator.py deleted file mode 100644 index 51c2db1..0000000 --- a/oxy/poc_translator.py +++ /dev/null @@ -1,48 +0,0 @@ -from ruamel.yaml import YAML, Any -from functools import reduce - - -yaml: YAML = YAML(typ="safe") - -out_string: str = "" -data: dict[str, Any] = yaml.load(open("mapping.yaml")) -print(data) -print(type(data)) - -function_name: str = "decode" + "_" + "_".join(data['string'].split(" ")) -args: str = "(data: &[u8])" -returnVal: str= " -> HashMap" - -signature: str = "pub fn " + function_name + args + returnVal + " {" -instantiate_hash_map: str = " let mut result = HashMap::new();" -conclusion: str = " result\n}" - -decodings: list[str] = [] -accumulated_size: int = 0 -for field in data["fields"]: # result.insert(1, (pd::big_endian(&data[0..2], 8) as f32) / 10.0); - field: dict - decoded: str - id = field["field_id"] - if field["size"] > 1: # we need to do some decoding, then - to_decode: str = f"&data[{accumulated_size}..{accumulated_size+field['size']}]" - _cf: str = field.get("correcting_factor", "") - correcting_factor: str = f"{' ' + ('/' if '/' in _cf else '*') + ' ' if 'correcting_factor' in field.keys() else ''}{_cf.split('/')[-1]}" - for decodingsetup in field["decoding"]: - decodingsetup: dict[str, dict[str, str]] = {k: reduce(lambda x,y: x|y, v, {}) for k,v in decodingsetup.items()} - for decoder, params in decodingsetup.items(): - match decoder: - case "big_endian": - to_decode = f"pd::big_endian({to_decode}, {params['bits']}) as {params['final_type']}" - case "twos_complement": - to_decode = f"pd::twos_comp({to_decode}, {params['bits']}) as {params['final_type']}" - decoded = f"{id}, {to_decode}{correcting_factor}" - else: # no decoding required! - decoded = f"{id}, data[{accumulated_size}] as {field['final_type']}" - - decodings.append(decoded) - accumulated_size += field["size"] - -formatted_decodings = [f" result.insert({i});" for i in decodings] - -finals: list[str] = [signature, instantiate_hash_map] + formatted_decodings + [conclusion] -print("\n".join(finals)) diff --git a/oxy/structs/CANField.py b/oxy/structs/CANField.py index bf32677..6ca53d1 100644 --- a/oxy/structs/CANField.py +++ b/oxy/structs/CANField.py @@ -1,20 +1,21 @@ from __future__ import annotations -from .CorrectingFactor import CorrectingFactor from .Decoding import * from ruamel.yaml import Optional from dataclasses import dataclass +from .Format import Format @dataclass class CANField: ''' - Represents a field in a CAN message. Has an id, a name, units, a size, + Represents a field in a CAN message. Has an id, a name, a unit, a size, and an optional CorrectingFactor and Decodings. Also knows its own index within its parent CANMsg, which is assigned at load from YAML. ''' id: int name: str - units: str + unit: str size: int index: int = -1 - correcting_factor: Optional[CorrectingFactor] = None + final_type: str = "f32" decodings: Optional[list[Decoding]] = None + format: Optional[str] = None diff --git a/oxy/structs/CANmsg.py b/oxy/structs/CANMsg.py similarity index 71% rename from oxy/structs/CANmsg.py rename to oxy/structs/CANMsg.py index d35664e..bdef91e 100644 --- a/oxy/structs/CANmsg.py +++ b/oxy/structs/CANMsg.py @@ -1,6 +1,4 @@ from __future__ import annotations -from ruamel.yaml import Optional, MappingNode -from structs.CorrectingFactor import CorrectingFactor from .CANField import CANField from dataclasses import dataclass @@ -9,15 +7,16 @@ class CANMsg: ''' Represents a CAN message. Has an id, a description, and a number of individual fields. ''' - id: int + id: str desc: str fields: list[CANField] def __post_init__(self) -> None: idx: int = 0 for field in self.fields: - field.index = idx - idx += field.size + if (field.index is not None): + field.index = idx + idx += field.size def __setstate__(self, state): diff --git a/oxy/structs/CorrectingFactor.py b/oxy/structs/CorrectingFactor.py deleted file mode 100644 index 530354c..0000000 --- a/oxy/structs/CorrectingFactor.py +++ /dev/null @@ -1,9 +0,0 @@ -from dataclasses import dataclass - -@dataclass -class CorrectingFactor: - ''' - Represents a correcting factor to be applied to data after decoding. - ''' - const: float - op: str diff --git a/oxy/structs/Decoding.py b/oxy/structs/Decoding.py index e461fbe..bf05872 100644 --- a/oxy/structs/Decoding.py +++ b/oxy/structs/Decoding.py @@ -7,17 +7,26 @@ class Decoding: that represents a decoding to be applied to a slice of data. ''' bits: int - final_type: str + entry_type: str repr: str = "*"*42 @dataclass class BigEndian(Decoding): repr: str = "big_endian" + entry_type = "&[u8]" @dataclass class LittleEndian(Decoding): repr: str = "little_endian" + entry_type = "&[u8]" @dataclass class TwosComplement(Decoding): repr: str = "twos_comp" + entry_type = "u32" + + +@dataclass +class Half(Decoding): + repr: str = "half" + entry_type = "u8" diff --git a/oxy/structs/Format.py b/oxy/structs/Format.py new file mode 100644 index 0000000..6e3bdcf --- /dev/null +++ b/oxy/structs/Format.py @@ -0,0 +1,56 @@ +from dataclasses import dataclass + +@dataclass +class Format: + ''' + Represents a format to be applied to data after decoding. + ''' + repr: str = "" + +@dataclass +class Temperature(Format): + repr: str = "temperature" + +@dataclass +class LowVoltage(Format): + repr: str = "low_voltage" + +@dataclass +class Torque(Format): + repr: str = "torque" + +@dataclass +class HighVoltage(Format): + repr: str = "high_voltage" + +@dataclass +class Current(Format): + repr: str = "current" + +@dataclass +class Angle(Format): + repr: str = "angle" + +@dataclass +class AngularVelocity(Format): + repr: str = "angular_velocity" + +@dataclass +class Frequency(Format): + repr: str = "frequency" + +@dataclass +class Power(Format): + repr: str = "power" + +@dataclass +class Timer(Format): + repr: str = "timer" + +@dataclass +class Flux(Format): + repr: str = "flux" + +@dataclass +class CellVoltage(Format): + repr: str = "cell_voltage" \ No newline at end of file diff --git a/oxy/structs/Messages.py b/oxy/structs/Messages.py new file mode 100644 index 0000000..a9cb99f --- /dev/null +++ b/oxy/structs/Messages.py @@ -0,0 +1,9 @@ +from dataclasses import dataclass +from .CANMsg import CANMsg + +@dataclass +class Messages: + ''' + Represents a list of CAN messages. Has a list of CANMsgs. + ''' + msgs: list[CANMsg] \ No newline at end of file diff --git a/oxy/structs/Result.py b/oxy/structs/Result.py new file mode 100644 index 0000000..662cd48 --- /dev/null +++ b/oxy/structs/Result.py @@ -0,0 +1,8 @@ + +class Result: + decode_data: str + master_mapping: str + + def __init__(self, decode_data: str, master_mapping: str): + self.decode_data = decode_data + self.master_mapping = master_mapping \ No newline at end of file diff --git a/oxy/typedpoc.py b/oxy/typedpoc.py index 89abd50..3aa2ba8 100644 --- a/oxy/typedpoc.py +++ b/oxy/typedpoc.py @@ -1,4 +1,21 @@ from YAMLParser import YAMLParser from RustSynth import RustSynth -print(RustSynth().synthesize(YAMLParser().parse(open("mapping.yaml", "r")))) +decode_data = open("../src/decode_data.rs", "w") +master_mapping = open("../src/master_mapping.rs", "w") + +bms_messages = YAMLParser().parse(open("yaml/bms.yaml", "r")) +mpu_messages = YAMLParser().parse(open("yaml/mpu.yaml", "r")) +wheel_messages = YAMLParser().parse(open("yaml/wheel.yaml", "r")) + + +bms_messages.msgs.extend(mpu_messages.msgs) +bms_messages.msgs.extend(wheel_messages.msgs) + +result = RustSynth().parse_messages(bms_messages.msgs) + +decode_data.write(result.decode_data) +decode_data.close() + +master_mapping.write(result.master_mapping) +master_mapping.close() \ No newline at end of file diff --git a/oxy/yaml/bms.yaml b/oxy/yaml/bms.yaml new file mode 100644 index 0000000..87ced71 --- /dev/null +++ b/oxy/yaml/bms.yaml @@ -0,0 +1,243 @@ +!Messages +msgs: +#BMS BROADCAST +- !CANMsg + id: "0x80" + desc: "accumulator status" + fields: + - !CANField + name: "BMS/Pack/Voltage" + unit: "V" + size: 2 + decodings: + - !BigEndian + bits: 8 + format: "high_voltage" + - !CANField + name: "BMS/Pack/Current" + unit: "A" + size: 2 + decodings: + - !BigEndian + bits: 8 + - !TwosComplement + bits: 16 + format: "current" + - !CANField + name: "BMS/Pack/Amp-hours" + unit: "Ah" + size: 2 + decodings: + - !BigEndian + bits: 8 + - !CANField + name: "BMS/Pack/SOC" + unit: "%" + size: 1 + - !CANField + name: "BMS/Pack/Health" + unit: "%" + size: 1 + +- !CANMsg + id: "0x81" + desc: "BMS Status" + fields: + - !CANField + name: "BMS/State" + unit: "" + size: 1 + - !CANField + name: "BMS/Faults" + unit: "" + size: 4 + decodings: + - !LittleEndian + bits: 8 + - !CANField + name: "BMS/Temps/Average" + unit: "C" + size: 1 + decodings: + - !TwosComplement + bits: 8 + - !CANField + name: "BMS/Temps/Internal" + size: 1 + unit: "C" + decodings: + - !TwosComplement + bits: 8 + - !CANField + name: "BMS/Cells/BurningStatus" + size: 1 + unit: "" + +- !CANMsg + id: "0x82" + desc: "Shutdown Control" + fields: + - !CANField + name: "BMS/Shutdown/MPE" + size: 1 + unit: "" + +- !CANMsg + id: "0x83" + desc: "Cell Data" + fields: + - !CANField + name: "BMS/Cells/Volts/High/Value" + size: 2 + unit: "V" + decodings: + - !LittleEndian + bits: 8 + format: "cell_voltage" + - !CANField + name: "BMS/Cells/Volts/High/Chip" + size: 1 + unit: "" + decodings: + - !Half + bits: 4 + - !CANField + name: "BMS/Cells/Volts/High/Cell" + index: 2 + size: 1 + unit: "" + decodings: + - !Half + bits: 0 + - !CANField + name: "BMS/Cells/Volts/Low/Value" + size: 2 + index: 3 + unit: "V" + decodings: + - !LittleEndian + bits: 8 + format: "cell_voltage" + - !CANField + name: "BMS/Cells/Volts/Low/Chip" + index: 5 + size: 1 + unit: "" + decodings: + - !Half + bits: 4 + - !CANField + name: "BMS/Cells/Volts/Low/Cell" + index: 5 + size: 1 + unit: "" + decodings: + - !Half + bits: 0 + - !CANField + name: "BMS/Cells/Volts/Ave/Value" + size: 2 + index: 6 + unit: "V" + decodings: + - !LittleEndian + bits: 8 + format: "cell_voltage" + +- !CANMsg + id: "0x84" + desc: "Cell Temperatures" + fields: + - !CANField + name: "BMS/Cells/Temp/High/Value" + unit: "C" + size: 2 + decodings: + - !LittleEndian + bits: 8 + - !TwosComplement + bits: 16 + - !CANField + name: "BMS/Cells/Temp/High/Cell" + unit: "" + size: 1 + decodings: + - !Half + bits: 4 + - !CANField + name: "BMS/Cells/Temp/High/Chip" + unit: "" + size: 1 + index: 2 + decodings: + - !Half + bits: 0 + - !CANField + name: "BMS/Cells/Temp/Low/Value" + unit: "C" + size: 2 + index: 3 + decodings: + - !LittleEndian + bits: 8 + - !TwosComplement + bits: 16 + - !CANField + name: "BMS/Cells/Temp/Low/Cell" + unit: "" + size: 1 + index: 5 + decodings: + - !Half + bits: 4 + - !CANField + name: "BMS/Cells/Temp/Low/Chip" + unit: "" + size: 1 + index: 5 + decodings: + - !Half + bits: 0 + - !CANField + name: "BMS/Cells/Temp/Ave/Value" + unit: "C" + size: 2 + index: 6 + decodings: + - !LittleEndian + bits: 8 + - !TwosComplement + bits: 16 + +- !CANMsg + id: "0x85" + desc: "Segment Temperatures" + fields: + - !CANField + name: "BMS/Segment/Temp/1" + unit: "C" + size: 1 + decodings: + - !TwosComplement + bits: 8 + - !CANField + name: "BMS/Segment/Temp/2" + unit: "C" + size: 1 + decodings: + - !TwosComplement + bits: 8 + - !CANField + name: "BMS/Segment/Temp/3" + unit: "C" + size: 1 + decodings: + - !TwosComplement + bits: 8 + - !CANField + name: "BMS/Segment/Temp/4" + unit: "C" + size: 1 + decodings: + - !TwosComplement + bits: 8 diff --git a/oxy/yaml/mpu.yaml b/oxy/yaml/mpu.yaml new file mode 100644 index 0000000..e3ff76e --- /dev/null +++ b/oxy/yaml/mpu.yaml @@ -0,0 +1,51 @@ +!Messages +msgs: +- !CANMsg + id: "0x500" + desc: "NERduino Acceleromter" + fields: + - !CANField + name: "MPU/Accel/X" + unit: "g" + size: 2 + decodings: + - !BigEndian + bits: 8 + format: "acceleration" + - !CANField + name: "MPU/Accel/Y" + unit: "g" + size: 2 + decodings: + - !BigEndian + bits: 8 + format: "acceleration" + - !CANField + name: "MPU/Accel/Z" + unit: "g" + size: 2 + decodings: + - !BigEndian + bits: 8 + format: "acceleration" + +- !CANMsg + id: "0x501" + desc: "MPU Status" + fields: + - !CANField + name: "MPU/State/Mode" + unit: "" + size: 1 + - !CANField + name: "MPU/State/Torque_Limit_Percentage" + unit: "" + size: 1 + - !CANField + name: "MPU/State/Regen_Strength" + unit: "" + size: 1 + - !CANField + name: "MPU/State/Traction_Control" + unit: "" + size: 1 \ No newline at end of file diff --git a/oxy/yaml/wheel.yaml b/oxy/yaml/wheel.yaml new file mode 100644 index 0000000..3f92646 --- /dev/null +++ b/oxy/yaml/wheel.yaml @@ -0,0 +1,14 @@ +!Messages +msgs: +- !CANMsg + id: "0x680" + desc: "Wheel State" + fields: + - !CANField + name: "WHEEL/Buttons/1" + unit: "" + size: 1 + - !CANField + name: "WHEEL/Buttons/2" + unit: "" + size: 1 \ No newline at end of file diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..41a4c85 --- /dev/null +++ b/src/client.rs @@ -0,0 +1,16 @@ +use crate::data::Data; + +/** + * Cummulative trait for structs that are able to connect to a server and publish data to it. + */ +pub trait Client { + /** + * Connects to the server at the given path. + */ + fn connect(&mut self, path: &str); + + /** + * Publishes the given data to the server. + */ + fn publish(&mut self, data: &Data); +} diff --git a/src/data.rs b/src/data.rs index e1b83cc..f13f818 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,142 +1,152 @@ -use chrono::prelude::*; use std::fmt; +/** + * Wrapper Class for Data coming off the car + */ pub struct Data { - // Wrapper class for an individual piece of data. - pub(crate) timestamp: DateTime, - pub id: u8, pub value: f32, + pub topic: String, + pub unit: String } +/** + * Implementation for the format of the data for debugging purposes + */ impl fmt::Display for Data { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Overrides the string representation of the class. - write!(f, "ID {} - {} - {}", self.id, self.timestamp, self.value) + write!(f, "topic: {}, value: {}, unit: {}", self.topic, self.value, self.unit) } } +/** + * Implementation fo the Data Structs' methods + */ impl Data { - pub fn new(timestamp: DateTime, id: u8, value: f32) -> Self { + /** + * Constructor + * @param id: the id of the data + * @param value: the value of the data + * @param topic: the topic of the data + */ + pub fn new(value: f32, topic: &str, unit: &str) -> Self { Self { - timestamp, - id, value, + topic: topic.to_string(), + unit: unit.to_string() } } -} -pub struct ProcessData { - // Utility functions to process message data. + pub fn to_json(&self) -> String { + format!("{{\"topic\": \"{}\", \"value\": {}, \"unit\": \"{}\"}}", self.topic, self.value, self.unit) + } } -impl ProcessData { - pub fn group_bytes(data_bytes: &[u8], group_length: usize) -> Vec> { - // Splits the given data bytes into lists of specified length. - data_bytes - .chunks(group_length) - .map(|chunk| chunk.to_vec()) - .collect() - } +/** + * Class to contain the data processing functions + */ +pub struct ProcessData {} +impl ProcessData { + /** + * Computes the twos complement of the given value. + */ pub fn twos_comp(val: u32, bits: usize) -> i64 { - // Computes the twos complement of the given value. if (val & (1 << (bits - 1))) != 0 { - (val as i64) - (1 << bits) + (val as i64) - ((1 << bits) as i64) } else { val as i64 } } + /** + * Transforms the given data bytes into a value in little endian. + * Little Endian byte order stores low order bytes first. + */ pub fn little_endian(data_bytes: &[u8], bits: usize) -> u32 { - // Transforms the given data bytes into a value in little endian. - // Little Endian byte order stores low order bytes first. let mut result: u32 = 0; for (i, byte) in data_bytes.iter().enumerate() { - // println!("Little End Byte: {}", byte); result |= (*byte as u32) << (bits * i); - // println!("Little End Result: {}", result) } result } + /** + * Transforms the given data bytes into a value in big endian. + * Big Endian byte order stores low order bytes last. + */ pub fn big_endian(bytes: &[u8], bits: usize) -> u32 { - // Transforms the given data bytes into a value in big endian. - // Big Endian byte order stores low order bytes last. let mut result: u32 = 0; for (i, byte) in bytes.iter().enumerate() { - // println!("Big End Byte: {}", byte); result |= (*byte as u32) << (bits * (bytes.len() - i - 1)); - // println!("Big End Result: {}", result); } result } - pub fn default_decode(byte_vals: &[u8]) -> Vec { - // Default decode structure seen by a majority of the messages. - - let grouped_vals = ProcessData::group_bytes(byte_vals, 2); - println!("CUCKED GROUP BYTES"); - let parsed_vals: Vec = grouped_vals - .iter() - .map(|val| ProcessData::little_endian(val, 8)) - .collect(); - println!("CUCKED LITTLE ENDIAN"); - let decoded_vals: Vec = parsed_vals - .iter() - .map(|val| ProcessData::twos_comp(*val, 16)) - .collect(); - println!("CUCKED TWOS COMP"); - decoded_vals - } + /** + * Decodes the given byte by taking the top four bits after shifting it by the given number of bits. + */ + pub fn half(byte: u8, bits: u8) -> u32 { + (byte >> bits & 15) as u32 + } } -pub struct FormatData { - // Utility functions to scale data values of a specific type. -} +/** + * Class to contain the data formatting functions + */ +pub struct FormatData {} impl FormatData { - pub fn temperature(value: i64) -> f32 { - value as f32 / 10.0 + pub fn temperature(value: f32) -> f32 { + value / 10.0 } - pub fn low_voltage(value: i64) -> f32 { - value as f32 / 100.0 + pub fn low_voltage(value: f32) -> f32 { + value / 100.0 } - pub fn torque(value: i64) -> f32 { - value as f32 / 10.0 + pub fn torque(value: f32) -> f32 { + value / 10.0 } - pub fn high_voltage(value: i64) -> f32 { - value as f32 / 10.0 + pub fn high_voltage(value: f32) -> f32 { + value / 10.0 } - pub fn current(value: i64) -> f32 { - value as f32 / 10.0 + pub fn current(value: f32) -> f32 { + value / 10.0 } - pub fn angle(value: i64) -> f32 { - value as f32 / 10.0 + pub fn angle(value: f32) -> f32 { + value / 10.0 } - pub fn angular_velocity(value: i64) -> i64 { + pub fn angular_velocity(value: f32) -> f32 { -value } - pub fn frequency(value: i64) -> f32 { - value as f32 / 10.0 + pub fn frequency(value: f32) -> f32 { + value / 10.0 + } + + pub fn power(value: f32) -> f32 { + value / 10.0 + } + + pub fn timer(value: f32) -> f32 { + value * 0.003 } - pub fn power(value: i32) -> f32 { - value as f32 / 10.0 + pub fn flux(value: f32) -> f32 { + value / 1000.0 } - pub fn timer(value: i32) -> f32 { - value as f32 * 0.003 + pub fn cell_voltage(value: f32) -> f32 { + value / 10000.0 } - pub fn flux(value: i64) -> f32 { - value as f32 / 1000.0 + pub fn acceleration(value: f32) -> f32 { + value * 0.0029 } } diff --git a/src/decode_data.rs b/src/decode_data.rs index 998eb5e..7d76103 100644 --- a/src/decode_data.rs +++ b/src/decode_data.rs @@ -1,449 +1,101 @@ -// This file specifies methods to decode messages into the many pieces of data they contain. +#![allow(clippy::all)] +use super::data::{Data,FormatData as fd, ProcessData as pd}; -use nalgebra::convert; -use nalgebra::{Matrix3, Vector3}; -use std::collections::HashMap; - -use super::data::FormatData as fd; -use super::data::ProcessData as pd; - -pub fn decode_mock(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert(0, 0.0); - result -} - -pub fn decode_accumulator_status(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert(1, (pd::big_endian(&data[0..2], 8) as f32) / 10.0); - result.insert( - 2, - pd::twos_comp(pd::big_endian(&data[2..4], 8) as u32, 16) as f32 / 10.0, - ); - result.insert(3, pd::big_endian(&data[4..6], 8) as f32); - result.insert(4, data[6] as f32); - result.insert(5, data[7] as f32); - result -} - -pub fn decode_bms_status(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert(106, data[0] as f32); - result.insert(107, pd::little_endian(&data[1..5], 8) as f32); - result.insert(10, pd::twos_comp(data[5] as u32, 8) as f32); - result.insert(11, pd::twos_comp(data[6] as u32, 8) as f32); - result.insert(143, data[7] as f32); - result -} - -pub fn decode3(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert(12, data[0] as f32); - result -} - -pub fn decode_cell_voltages(data: &[u8]) -> HashMap { - let high_cell_volt_chip_number = (data[2] >> 4) & 15; - let high_cell_volt_cell_number = (data[2] >> 0) & 15; - let low_cell_volt_chip_number = (data[5] >> 4) & 15; - let low_cell_volt_cell_number = (data[5] >> 0) & 15; - let mut result = HashMap::new(); - result.insert(13, (pd::little_endian(&data[0..2], 8) as f32) / 10000.0); - result.insert(121, high_cell_volt_chip_number as f32); - result.insert(122, high_cell_volt_cell_number as f32); - result.insert(15, (pd::little_endian(&data[3..5], 8) as f32) / 10000.0); - result.insert(123, low_cell_volt_chip_number as f32); - result.insert(124, low_cell_volt_cell_number as f32); - result.insert(17, (pd::little_endian(&data[6..8], 8) as f32) / 10000.0); - result -} - -pub fn decode5(data: &[u8]) -> HashMap { - let decoded_data = pd::default_decode(data); - let final_data = decoded_data - .iter() - .map(|d| fd::temperature(*d)) - .collect::>(); - let mut result = HashMap::new(); - result.insert(18, final_data[0]); - result.insert(19, final_data[1]); - result.insert(20, final_data[2]); - result.insert(21, final_data[3]); - result -} - -pub fn decode6(data: &[u8]) -> HashMap { - let decoded_data = pd::default_decode(data); - let final_data = decoded_data - .iter() - .map(|d| fd::temperature(*d)) - .collect::>(); - let mut result = HashMap::new(); - result.insert(22, final_data[0]); - result.insert(23, final_data[1]); - result.insert(24, final_data[2]); - result.insert(25, final_data[3]); - result -} - -pub fn decode7(data: &[u8]) -> HashMap { - let decoded_data = pd::default_decode(data); - let final_data = decoded_data[..3] - .iter() - .map(|d| fd::temperature(*d)) - .collect::>(); - let mut result = HashMap::new(); - result.insert(26, final_data[0]); - result.insert(27, final_data[1]); - result.insert(28, final_data[2]); - result.insert(29, fd::torque(decoded_data[3])); - result -} - -// TODO: Fill this method out (complicated with bit shifts) -pub fn decode8(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert(30, 0.0); - result.insert(31, 0.0); - result.insert(32, 0.0); - result.insert(33, 0.0); - result.insert(34, 0.0); - result.insert(35, 0.0); - result -} - -pub fn decode9(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert(36, data[0] as f32); - result.insert(37, data[1] as f32); - result.insert(38, data[2] as f32); - result.insert(39, data[3] as f32); - result.insert(40, data[4] as f32); - result.insert(41, data[5] as f32); - result.insert(42, data[6] as f32); - result.insert(43, data[7] as f32); - result -} - -pub fn decode10(data: &[u8]) -> HashMap { - let decoded_data = pd::default_decode(data); - let motor_speed: f32 = fd::angular_velocity(decoded_data[1]) as f32; - let vehicle_speed = motor_speed * 0.013048225; - let mut result = HashMap::new(); - result.insert(44, fd::angle(decoded_data[0])); - result.insert(45, motor_speed); - result.insert(46, fd::frequency(decoded_data[2])); - result.insert(47, fd::angle(decoded_data[3])); - result.insert(101, vehicle_speed); - result -} - -pub fn decode11(data: &[u8]) -> HashMap { - let decoded_data = pd::default_decode(data); - let final_data = decoded_data - .iter() - .map(|d| fd::current(*d)) - .collect::>(); - let mut result = HashMap::new(); - result.insert(48, final_data[0]); - result.insert(49, final_data[1]); - result.insert(50, final_data[2]); - result.insert(51, final_data[3]); - result -} - -pub fn decode12(data: &[u8]) -> HashMap { - let decoded_data = pd::default_decode(&data); - let final_data: Vec = decoded_data.iter().map(|d| fd::high_voltage(*d)).collect(); - let mut result = HashMap::new(); - result.insert(52, final_data[0]); - result.insert(53, final_data[1]); - result.insert(54, final_data[2]); - result.insert(55, final_data[3]); - result -} - -pub fn decode13(data: &[u8]) -> HashMap { - let decoded_data = pd::default_decode(&data); - let mut result = HashMap::new(); - result.insert(56, fd::flux(decoded_data[0])); - result.insert(57, fd::flux(decoded_data[1])); - result.insert(58, fd::current(decoded_data[2])); - result.insert(59, fd::current(decoded_data[3])); - result -} - -pub fn decode14(data: &[u8]) -> HashMap { - let decoded_data = pd::default_decode(&data); - let final_data: Vec = decoded_data.iter().map(|d| fd::low_voltage(*d)).collect(); - let mut result = HashMap::new(); - result.insert(60, final_data[0]); - result.insert(61, final_data[1]); - result.insert(62, final_data[2]); - result.insert(63, final_data[3]); - result -} - -pub fn decode15(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert(64, pd::little_endian(&data[0..2], 8) as f32); - result.insert(65, data[2] as f32); - result.insert(66, data[3] as f32); - result.insert(67, (data[4] & 1) as f32); - result.insert(68, ((data[4] >> 5) & 7) as f32); - result.insert(69, (data[5] & 1) as f32); - result.insert(70, (data[6] & 1) as f32); - result.insert(71, ((data[6] >> 7) & 1) as f32); - result.insert(72, (data[7] & 1) as f32); - result.insert(73, ((data[7] >> 1) & 1) as f32); - result.insert(74, ((data[7] >> 2) & 1) as f32); - result -} - -pub fn decode16(data: &[u8]) -> HashMap { - let binding = pd::group_bytes(&data, 2); - let data = binding.iter().map(|d| pd::little_endian(d, 8) as f32); - let grouped_data = data.collect::>(); - let mut result = HashMap::new(); - result.insert(75, grouped_data[0]); - result.insert(76, grouped_data[1]); - result.insert(77, grouped_data[2]); - result.insert(78, grouped_data[3]); - result -} - -pub fn decode17(data: &[u8]) -> HashMap { - let decoded_data = pd::default_decode(&data[0..4]); - let timer_data = pd::little_endian(&data[4..], 8) as i32; - let mut result = HashMap::new(); - result.insert(79, fd::torque(decoded_data[0])); - result.insert(80, fd::torque(decoded_data[1])); - result.insert(81, fd::timer(timer_data)); - result -} - -pub fn decode18(data: &[u8]) -> HashMap { - let decoded_data = pd::default_decode(&data); - let mut result = HashMap::new(); - result.insert(82, fd::torque(decoded_data[0])); - result.insert(83, fd::angular_velocity(decoded_data[1]) as f32); - result.insert(84, data[4] as f32); - result.insert(85, (data[5] & 1) as f32); - result.insert(86, ((data[5] >> 1) & 1) as f32); - result.insert(87, ((data[5] >> 2) & 1) as f32); - result.insert(88, fd::torque(decoded_data[3])); - result -} - -pub fn decode19(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert(89, pd::little_endian(&data[0..2], 8) as f32); - result.insert(90, pd::little_endian(&data[2..4], 8) as f32); - result -} - -pub fn decode_accelerometer_data(data: &[u8]) -> HashMap { - let decoded_data = pd::default_decode(&data); - let converted_data = decoded_data - .iter() - .map(|val| *val as f32 * 0.0029) - .collect::>(); - let matrix_data: Vector3 = - Vector3::new(converted_data[0], converted_data[1], converted_data[2]); - let transform_matrix = Matrix3::new( - 1.0, - 0.0, - 0.0, - 0.0, - f32::cos(f32::to_radians(70.0)), - f32::sin(f32::to_radians(70.0)), - 0.0, - -f32::sin(f32::to_radians(70.0)), - f32::cos(f32::to_radians(70.0)), - ); - let transformed_data = transform_matrix * matrix_data; - let mut result = HashMap::new(); - result.insert(91, transformed_data[0]); - result.insert(92, transformed_data[1]); - result.insert(93, transformed_data[2]); - result -} - -pub fn decode21(data: &[u8]) -> HashMap { - let temp = pd::little_endian(&data[0..2], 8) as f32; - let humid = pd::little_endian(&data[2..4], 8) as f32; - let temp_f = -49.0 + (315.0 * temp / 65535.0); - let temp_c = -45.0 + (175.0 * temp / 65535.0); - let rel_humid = 100.0 * humid / 65535.0; - let mut result = HashMap::new(); - result.insert(94, temp_c); - result.insert(95, temp_f); - result.insert(96, rel_humid); - result -} - -// fn decode22(data: &[u8]) -> HashMap { -// let cell_id = data[0] as u32; -// let instant_voltage = pd::big_endian(&data[1..3], 8); -// let internal_resistance = (pd::big_endian(&data[3..5], 8) & 32767) as u32; -// let shunted = ((data[3] >> 7) & 1) as u32; -// let open_voltage = pd::big_endian(&data[5..7], 8); -// let mut result = HashMap::new(); -// result.insert( -// 97, -// "Cell ID: ".to_string() -// + &cell_id.to_string() -// + ", Instant Voltage: " -// + &instant_voltage.to_string() -// + ", Internal Resistance: " -// + &internal_resistance.to_string() -// + ", Shunted: " -// + &shunted.to_string() -// + ", Open Voltage: " -// + &open_voltage.to_string(), -// ); - -// result -// } - -pub fn decode29(data: &[u8]) -> HashMap { - let glv_current = pd::twos_comp(pd::little_endian(data, 8), 32) as f32; - let mut result = HashMap::new(); - result.insert(98, glv_current / 1000000.0); +pub fn decode_mock(_data: &[u8]) -> Vec:: { + let result = vec![ + Data::new(0.0, "Mock", "") + ]; result } - -pub fn decode34(data: &[u8]) -> HashMap { - let voltage1 = pd::twos_comp(pd::little_endian(&data[0..4], 8), 32) as f32; - let voltage2 = pd::twos_comp(pd::little_endian(&data[4..], 8), 32) as f32; - let mut result = HashMap::new(); - result.insert(99, voltage1 / 1000000.0); - result.insert(100, voltage2 / 1000000.0); +pub fn decode_accumulator_status(data: &[u8]) -> Vec:: { + let result = vec![ + Data::new(fd::high_voltage(pd::big_endian(&data[0..2] as &[u8], 8) as f32), "BMS/Pack/Voltage", "V"), + Data::new(fd::current(pd::twos_comp(pd::big_endian(&data[2..4] as &[u8], 8) as u32, 16) as f32), "BMS/Pack/Current", "A"), + Data::new(pd::big_endian(&data[4..6] as &[u8], 8) as f32, "BMS/Pack/Amp-hours", "Ah"), + Data::new(data[6] as f32, "BMS/Pack/SOC", "%"), + Data::new(data[7] as f32, "BMS/Pack/Health", "%"), + ]; result } -pub fn decode35(data: &[u8]) -> HashMap { - let mut result: HashMap = HashMap::new(); - result.insert(102, pd::big_endian(&data[0..2], 8) as f32); - result.insert(103, pd::big_endian(&data[2..4], 8) as f32); - result.insert(104, data[4] as f32); +pub fn decode_bms_status(data: &[u8]) -> Vec:: { + let result = vec![ + Data::new(data[0] as f32, "BMS/State", ""), + Data::new(pd::little_endian(&data[1..5] as &[u8], 8) as f32, "BMS/Faults", ""), + Data::new(pd::twos_comp(data[5] as u32, 8) as f32, "BMS/Temps/Average", "C"), + Data::new(pd::twos_comp(data[6] as u32, 8) as f32, "BMS/Temps/Internal", "C"), + Data::new(data[7] as f32, "BMS/Cells/BurningStatus", ""), + ]; result } -pub fn decode_mpu_dashboard_info(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert(105, data[0] as f32); - result.insert(130, data[1] as f32); - result.insert(131, data[2] as f32); - result.insert(132, data[3] as f32); - result.insert(133, data[4] as f32); - result.insert(144, data[5] as f32); - result.insert(145, data[6] as f32); - result.insert(146, data[7] as f32); +pub fn decode_shutdown_control(data: &[u8]) -> Vec:: { + let result = vec![ + Data::new(data[0] as f32, "BMS/Shutdown/MPE", ""), + ]; result } -pub fn decode_gps_1(data: &[u8]) -> HashMap { - let longitude = pd::twos_comp(pd::little_endian(&data[0..4], 8), 32) as f32 / 10000000.0; - let latitude = pd::twos_comp(pd::little_endian(&data[4..8], 8), 32) as f32 / 10000000.0; - let mut result = HashMap::new(); - result.insert(108, longitude); - result.insert(109, latitude); +pub fn decode_cell_data(data: &[u8]) -> Vec:: { + let result = vec![ + Data::new(fd::cell_voltage(pd::little_endian(&data[0..2] as &[u8], 8) as f32), "BMS/Cells/Volts/High/Value", "V"), + Data::new(pd::half(data[2] as u8, 4) as f32, "BMS/Cells/Volts/High/Chip", ""), + Data::new(pd::half(data[3] as u8, 0) as f32, "BMS/Cells/Volts/High/Cell", ""), + Data::new(fd::cell_voltage(pd::little_endian(&data[4..6] as &[u8], 8) as f32), "BMS/Cells/Volts/Low/Value", "V"), + Data::new(pd::half(data[6] as u8, 4) as f32, "BMS/Cells/Volts/Low/Chip", ""), + Data::new(pd::half(data[7] as u8, 0) as f32, "BMS/Cells/Volts/Low/Cell", ""), + Data::new(fd::cell_voltage(pd::little_endian(&data[8..10] as &[u8], 8) as f32), "BMS/Cells/Volts/Ave/Value", "V"), + ]; result } -pub fn decode_gps_2(data: &[u8]) -> HashMap { - let altitude = pd::twos_comp(pd::little_endian(&data[4..8], 8), 32) as f32 / 1000.0; - let mut result: HashMap = HashMap::new(); - result.insert( - 110, - pd::twos_comp(pd::little_endian(&data[0..4], 8), 32) as f32, - ); - result.insert(111, altitude); +pub fn decode_cell_temperatures(data: &[u8]) -> Vec:: { + let result = vec![ + Data::new(pd::twos_comp(pd::little_endian(&data[0..2] as &[u8], 8) as u32, 16) as f32, "BMS/Cells/Temp/High/Value", "C"), + Data::new(pd::half(data[2] as u8, 4) as f32, "BMS/Cells/Temp/High/Cell", ""), + Data::new(pd::half(data[3] as u8, 0) as f32, "BMS/Cells/Temp/High/Chip", ""), + Data::new(pd::twos_comp(pd::little_endian(&data[4..6] as &[u8], 8) as u32, 16) as f32, "BMS/Cells/Temp/Low/Value", "C"), + Data::new(pd::half(data[6] as u8, 4) as f32, "BMS/Cells/Temp/Low/Cell", ""), + Data::new(pd::half(data[7] as u8, 0) as f32, "BMS/Cells/Temp/Low/Chip", ""), + Data::new(pd::twos_comp(pd::little_endian(&data[8..10] as &[u8], 8) as u32, 16) as f32, "BMS/Cells/Temp/Ave/Value", "C"), + ]; result } -pub fn decode_gps_3(data: &[u8]) -> HashMap { - let ground_speed = pd::twos_comp(pd::little_endian(&data[0..4], 8), 32) as f32 / 1000.0; - let heading = pd::twos_comp(pd::little_endian(&data[4..8], 8), 32) as f32 / 100000.0; - let mut result = HashMap::new(); - result.insert(112, ground_speed); - result.insert(113, heading); - result -} - -pub fn decode_cell_temps(data: &[u8]) -> HashMap { - let high_cell_temp_chip_number = (data[2] >> 4) & 15; - let high_cell_temp_cell_number = (data[2] >> 0) & 15; - let low_cell_temp_chip_number = (data[5] >> 4) & 15; - let low_cell_temp_cell_number = (data[5] >> 0) & 15; - - let mut result = HashMap::new(); - result.insert( - 114, - pd::twos_comp(pd::little_endian(&data[0..2], 8), 16) as f32, - ); - result.insert(115, high_cell_temp_chip_number as f32); - result.insert(116, high_cell_temp_cell_number as f32); - result.insert( - 117, - pd::twos_comp(pd::little_endian(&data[3..5], 8), 16) as f32, - ); - result.insert(118, low_cell_temp_chip_number as f32); - result.insert(119, low_cell_temp_cell_number as f32); - result.insert( - 120, - pd::twos_comp(pd::little_endian(&data[6..8], 8), 16) as f32, - ); - +pub fn decode_segment_temperatures(data: &[u8]) -> Vec:: { + let result = vec![ + Data::new(pd::twos_comp(data[0] as u32, 8) as f32, "BMS/Segment/Temp/1", "C"), + Data::new(pd::twos_comp(data[1] as u32, 8) as f32, "BMS/Segment/Temp/2", "C"), + Data::new(pd::twos_comp(data[2] as u32, 8) as f32, "BMS/Segment/Temp/3", "C"), + Data::new(pd::twos_comp(data[3] as u32, 8) as f32, "BMS/Segment/Temp/4", "C"), + ]; result } -pub fn decode_segment_temps(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert(125, pd::twos_comp(data[0] as u32, 8) as f32); - result.insert(126, pd::twos_comp(data[1] as u32, 8) as f32); - result.insert(127, pd::twos_comp(data[2] as u32, 8) as f32); - result.insert(128, pd::twos_comp(data[3] as u32, 8) as f32); - +pub fn decode_nerduino_acceleromter(data: &[u8]) -> Vec:: { + let result = vec![ + Data::new(fd::acceleration(pd::big_endian(&data[0..2] as &[u8], 8) as f32), "MPU/Accel/X", "g"), + Data::new(fd::acceleration(pd::big_endian(&data[2..4] as &[u8], 8) as f32), "MPU/Accel/Y", "g"), + Data::new(fd::acceleration(pd::big_endian(&data[4..6] as &[u8], 8) as f32), "MPU/Accel/Z", "g"), + ]; result } -pub fn decode_logging_status(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert(129, data[0] as f32); - +pub fn decode_mpu_status(data: &[u8]) -> Vec:: { + let result = vec![ + Data::new(data[0] as f32, "MPU/State/Mode", ""), + Data::new(data[1] as f32, "MPU/State/Torque_Limit_Percentage", ""), + Data::new(data[2] as f32, "MPU/State/Regen_Strength", ""), + Data::new(data[3] as f32, "MPU/State/Traction_Control", ""), + ]; result } -pub fn decode_lv_battery_1(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert(134, (pd::little_endian(&data[0..2], 8)) as f32); - result.insert(135, data[2] as f32); - result.insert(136, (pd::little_endian(&data[3..5], 8)) as f32); - result.insert(137, data[5] as f32); - result.insert(138, (pd::little_endian(&data[6..8], 8)) as f32); +pub fn decode_wheel_state(data: &[u8]) -> Vec:: { + let result = vec![ + Data::new(data[0] as f32, "WHEEL/Buttons/1", ""), + Data::new(data[1] as f32, "WHEEL/Buttons/2", ""), + ]; result } -pub fn decode_lv_battery_2(data: &[u8]) -> HashMap { - let mut result = HashMap::new(); - result.insert( - 139, - (pd::twos_comp(pd::little_endian(&data[0..2], 8), 16) as f32) * (192.264 / 1000000.0) * 4.0, - ); - result.insert( - 140, - (pd::twos_comp(pd::little_endian(&data[2..4], 8), 16) as f32) * (1.648 / 1000.0), - ); - result.insert( - 141, - (pd::twos_comp(pd::little_endian(&data[4..6], 8), 16) as f32) * (1.648 / 1000.0), - ); - result.insert( - 142, - (pd::twos_comp(pd::little_endian(&data[6..8], 8), 16) as f32) * (1.46487 / 1000000.0) - / (0.5 / 1000.0), - ); - result -} diff --git a/src/decode_files.rs b/src/decode_files.rs deleted file mode 100644 index a25629b..0000000 --- a/src/decode_files.rs +++ /dev/null @@ -1,93 +0,0 @@ - -// This file specifies methods to decode message fields (timestamp, id, data bytes) from -// a line in a log file. - - -use chrono::prelude::*; -use std::convert::TryInto; - -use message::Message; - -mod message; - -enum LogFormat { - Textual1, - Textual1Legacy, - Textual2, - Binary, -} - -fn process_line(line: &str, format: LogFormat) -> Message { - - // Processes a line of textual data according to a given format. - - match format { - LogFormat::Textual1 => _process_textual1(line), - LogFormat::Textual1Legacy => _process_textual1_legacy(line), - LogFormat::Textual2 => _process_textual2(line), - LogFormat::Binary => _process_binary(line), - } -} - -fn _process_textual1(line: &str) -> Message { - - // Processes a line of data in the format "Timestamp id length [data1,data2,...]" - // Example line format: 1679511802367 514 8 [54,0,10,0,0,0,0,0] - - let fields: Vec<&str> = line.trim().split(' ').collect(); - let timestamp = NaiveDateTime::from_timestamp(fields[0].parse::().unwrap() / 1000, 0); - let id = fields[1].parse::().unwrap(); - let length = fields[2].parse::().unwrap(); - let data: Vec = fields[3][1..fields[3].len() - 1] - .split(',') - .map(|x| x.parse().unwrap()) - .collect(); - // remove commas and brackets at start and end - let int_data: Vec = data - .chunks_exact(2) - .map(|x| i16::from_le_bytes(x.try_into().unwrap())) - .collect(); - Message::new(timestamp, id, int_data) -} - -fn _process_textual1_legacy(line: &str) -> Message { - - // Processes a line of data in the format: Timestamp id length [data1,data2,...] - // Example line format: 2021-01-01T00:00:00.003Z 514 8 [54,0,10,0,0,0,0,0] - - let fields: Vec<&str> = line.trim().split(' ').collect(); - let timestamp = NaiveDateTime::parse_from_str(fields[0], "%Y-%m-%dT%H:%M:%S.%fZ") - .unwrap() - .into(); - let id = fields[1].parse::().unwrap(); - let length = fields[2].parse::().unwrap(); - let data: Vec = fields[3][1..fields[3].len() - 1] - .split(',') - .map(|x| x.parse().unwrap()) - .collect(); - let int_data: Vec = data - .chunks_exact(2) - .map(|x| i16::from_le_bytes(x.try_into().unwrap())) - .collect(); - Message::new(timestamp, id, int_data) -} - -fn _process_textual2(line: &str) -> Message { - - // Processes a line of data in the format "Timestamp id length data1 data2 ..." - // Example line format: 1659901910.121 514 8 54 0 10 0 0 0 0 0 - - let fields: Vec<&str> = line.trim().split(' ').collect(); - let timestamp = NaiveDateTime::from_timestamp(fields[0].parse::().unwrap(), 0); - let id = fields[1].parse::().unwrap(); - let length = fields[2].parse::().unwrap(); - let data: Vec = fields[3..3 + length] - .iter() - .map(|x| x.parse().unwrap()) - .collect(); - Message::new(timestamp, id, data) -} - -fn _process_binary(line: &str) -> Message { - panic!("Binary files not currently supported.") -} \ No newline at end of file diff --git a/src/decode_statuses.rs b/src/decode_statuses.rs deleted file mode 100644 index de2a88f..0000000 --- a/src/decode_statuses.rs +++ /dev/null @@ -1,163 +0,0 @@ -use std::collections::HashMap; - -// Mapping from data IDs to the status bits they encode -// Each data ID contains a hash map with keys that are bit names and values that are the indexes -const STATUS_MAP: HashMap> = [(6, HashMap::new()), // Failsafe Statuses -(7, HashMap::new()), // DTC Status 1 -(8, HashMap::new()), // DTC Status 2 -(9, HashMap::new()), // Current Limits Status -(12, HashMap::new()), // MPE State -( // VSM State - 64, - hashmap![ - "VSM Start State" => 0, - "Pre-charge Init State" => 1, - "Pre-charge Active State" => 2, - "Pre-charge Complete State" => 3, - "VSM Wait State" => 4, - "VSM Ready State" => 5, - "Motor Running State" => 6, - "Blink Fault Code State" => 7 - ] -), -( // Inverter State - 65, - hashmap![ - "Power on State" => 0, - "Stop State" => 1, - "Open Loop State" => 2, - "Closed Loop State" => 3, - "Wait State" => 4, - "Idle Run State" => 8, - "Idle Stop State" => 9 - ] -), -( // Relay State - 66, - hashmap![ - "Relay 1 Status" => 0, - "Relay 2 Status" => 1, - "Relay 3 Status" => 2, - "Relay 4 Status" => 3, - "Relay 5 Status" => 4, - "Relay 6 Status" => 5, - ] -), -(// Inverter Run Mode - 67, hashmap!["Inverter Run Mode" => 0]), -(// Inverter Command Mode - 69, hashmap!["Inverter Command Mode" => 0]), -(// Inverter Enable State - 70, hashmap!["Inverter Enable State" => 0]), -(// Inverter Enable Lockout - 71, hashmap!["Inverter Enable Lockout" => 0]), -(// Direction Command - 72, hashmap!["Direction Command" => 0]), -(// BMS Active - 73, hashmap!["BMS Active" => 0]), -(// BMS Limiting Torque - 74, hashmap!["BMS Limiting Torque" => 0]), -(// POST Fault Lo - 75, - hashmap![ - "Hardware Gate/Desaturation Fault" => 0, - "HW Over-current Fault" => 1, - "Accelerator Shorted" => 2, - "Accelerator Open" => 3, - "Current Sensor Low" => 4, - "Current Sensor High" => 5, - "Module Temperature Low" => 6, - "Module Temperature High" => 7, - "Control PCB Temperature Low" => 8, - "Control PCB Temperature High" => 9, - "Gate Drive PCB Temperature Low" => 10, - "Gate Drive PCB Temperature High" => 11, - "5V Sense Voltage Low" => 12, - "5V Sense Voltage High" => 13, - "12V Sense Voltage Low" => 14, - "12V Sense Voltage High" => 15 - ]), -( - 76, - hashmap![ // POST Fault Hi - "2.5V Sense Voltage Low" => 0, - "2.5V Sense Voltage High" => 1, - "1.5V Sense Voltage Low" => 2, - "1.5V Sense Voltage High" => 3, - "DC Bus Voltage High" => 4, - "DC Bus Voltage Low" => 5, - "Pre-charge Timeout" => 6, - "Pre-charge Voltage Failure" => 7, - "Brake Shorted" => 14, - "Brake Open" => 15 - ]), -( - 77, - hashmap![ // Run Fault Lo - "Motor Over-speed Fault" => 0, - "Over-current Fault" => 1, - "Over-voltage Fault" => 2, - "Inverter Over-temperature Fault" => 3, - "Accelerator Input Shorted Fault" => 4, - "Accelerator Input Open Fault" => 5, - "Direction Command Fault" => 6, - "Inverter Response Time-out Fault" => 7, - "Hardware Gate/Desaturation Fault" => 8, - "Hardware Over-current Fault" => 9, - "Under-voltage Fault" => 10, - "CAN Command Message Lost Fault" => 11, - "Motor Over-temperature Fault" => 12 - ]), -( - 78, - hashmap![ // Run Fault Hi - "Brake Input Shorted Fault" => 0, - "Brake Input Open Fault" => 1, - "Module A Over-temperature Fault" => 2, - "Module B Over temperature Fault" => 3, - "Module C Over-temperature Fault" => 4, - "PCB Over-temperature Fault" => 5, - "Gate Drive Board 1 Over-temperature Fault" => 6, - "Gate Drive Board 2 Over-temperature Fault" => 7, - "Gate Drive Board 3 Over-temperature Fault" => 8, - "Current Sensor Fault" => 9, - "Hardware Over-Voltage Fault" => 11, - "Resolver Not Connected" => 14, - "Inverter Discharge Active" => 15 - ]), -(// Direction Command - 84, hashmap!["Direction Command" => 0]), -(// Inverter Enable - 85, hashmap!["Inverter Enable" => 0]), -(// Inverter Discharge - 86, hashmap!["Inverter Discharge" => 0]), -(// Speed Mode Enable - 87, hashmap!["Speed Mode Enable" => 0]), -(// Cell Voltage Info -)]; - - -fn get_status(data: &Data, name: &str) -> i32 { - if !STATUS_MAP.contains_key(&data.id) { - panic!("Data ID has no associated status mapping"); - } - let bitmap = &STATUS_MAP[&data.id]; - - if !bitmap.contains_key(name) { - panic!("Status name could not be found in the given data point"); - } - let index = bitmap[name]; - - return (data.value >> index) & 1; -} - -fn get_statuses(data: &Data) -> HashMap<&str, i32> { - if !STATUS_MAP.contains_key(&data.id) { - panic!("Data ID has no associated status mapping"); - } - let bitmap = &STATUS_MAP[&data.id]; - - // Convert each dict value to the bit value at the index - return bitmap.iter().map(|(name, index)| (*name, (data.value >> index) & 1)).collect(); -} - diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..455565d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,6 @@ +pub mod client; +pub mod data; +pub mod decode_data; +pub mod message; +pub mod mqtt; +pub mod master_mapping; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b3ca34b..cf414e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,31 +1,19 @@ -extern crate systemstat; -use chrono::DateTime; -use chrono::TimeZone; -use chrono::Utc; -use socketcan::*; -use std::env; -use std::io::Write; -use std::os::unix::net::UnixStream; -use std::process::Command; -use std::sync::mpsc::channel; -use std::thread; -mod data; -mod decode_data; -mod master_mapping; -mod message; +use std::{ + env, + process::{self, Command}, +}; -fn main() { - let args: Vec = env::args().collect(); - let default = "tmp/ipc.sock".to_owned(); - let ipc_path = args.get(0).unwrap_or(&default); +use calypso::{client::Client, message::Message, mqtt::MqttClient}; +use socketcan::CANSocket; +fn configure_can() { let mut down_command = Command::new("sudo") .arg("ifconfig") .arg("can0") .arg("down") .spawn() .expect("down command did not work"); - down_command + down_command // Takes down any current can networks .wait() .expect("Fail while waiting for down command"); let mut bit_rate_commmand = Command::new("sudo") @@ -39,7 +27,7 @@ fn main() { .arg("1000000") .spawn() .expect("bit rate command did not work"); - bit_rate_commmand + bit_rate_commmand //sets the bit rate of the can network .wait() .expect("Fail while waiting for bit rate"); let mut up_command = Command::new("sudo") @@ -48,15 +36,18 @@ fn main() { .arg("up") .spawn() .expect("up command did nto work"); - up_command + up_command // Brings up the new can network .wait() .expect("Fail while waiting for up command"); +} - let mut stream = UnixStream::connect(ipc_path).unwrap(); - let (tx, rx) = channel(); +/** + * Reads the can socket and publishes the data to the given client. + */ +fn read_can(mut publisher: Box) { //open can socket channel at name can0 const CAN_CHANNEL: &str = "can0"; - let socket = CANSocket::open(&CAN_CHANNEL); + let socket = CANSocket::open(CAN_CHANNEL); let socket = match socket { Ok(socket) => socket, Err(err) => { @@ -64,28 +55,61 @@ fn main() { return; } }; - thread::spawn(move || loop { - let msg = socket.read_frame().unwrap(); - let date: DateTime = Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(); + loop { + let msg = match { socket.read_frame() } { + Ok(msg) => msg, + Err(err) => { + println!("Failed to read CAN frame: {}", err); + continue; + } + }; let data = msg.data(); - let message = message::Message::new(&date, &msg.id(), &data); + let message = Message::new(msg.id(), data.to_vec()); let decoded_data = message.decode(); - for (_i, data) in decoded_data.iter().enumerate() { - let message = format!( - "{{ - index:{}, - value:{} - }}", - data.id.to_string(), - data.value.to_string() - ); - println!("Sending message: {}", message); - tx.send(message).unwrap(); + for data in decoded_data.iter() { + publisher.publish(data) + } + } +} + +/** + * Parses the command line arguments. + * Returns the client type and the path to connect to. + */ +fn parse_args() -> (String, Box) { + let args: Vec = env::args().collect(); + println!("{:?}", args); + if args.len() < 2 { + println!("Please provide a client type"); + process::exit(1); + } + let client_type = &args[1]; + let path = &args[2]; + + println!("Client type: {}", client_type); + println!("Path: {}", path); + + match client_type.as_str() { + "mqtt" => ( + String::from(path), + Box::new(MqttClient::new()) as Box, + ), + _ => { + println!("Please provide a valid client type"); + process::exit(1); } - }); - loop { - let _ = rx - .try_recv() - .map(|reply| stream.write_all(reply.as_bytes())); } } + +/** + * Main Function + * Configures the can network, retrieves the client based on the command line arguments, connects the client and then reads the can socket. + * Sample Calls for IPC "/home/ner/Desktop/Calypso/target/release/calypso ipc /tmp/ipc.sock &" + * Sample Call for Mqtt "/home/ner/Desktop/Calypso/target/release/calypso mqtt localhost:1883 &" + */ +fn main() { + configure_can(); + let (path, mut client) = parse_args(); + client.connect(&path); + read_can(client); +} diff --git a/src/master_mapping.rs b/src/master_mapping.rs index d5d7fa1..95a5b7a 100644 --- a/src/master_mapping.rs +++ b/src/master_mapping.rs @@ -1,247 +1,27 @@ -use super::decode_data::*; -use std::collections::HashMap; +use super::decode_data::*; +use super::data::Data; -#[derive(Clone)] pub struct MessageInfo { - pub description: String, - pub decoder: fn(data: &[u8]) -> HashMap, -} + pub decoder: fn(data: &[u8]) -> Vec, +} impl MessageInfo { - pub fn new(description: String, decoder: fn(data: &[u8]) -> HashMap) -> Self { + pub fn new(decoder: fn(data: &[u8]) -> Vec) -> Self { Self { - description, - decoder, + decoder } } } - -// Mapping from external message ID to decoding information -pub fn get_message_info(id: &u32) -> MessageInfo { - match id { - 1 => return MessageInfo::new("accumulator status".to_string(), decode_accumulator_status), - 2 => return MessageInfo::new("BMS status".to_string(), decode_bms_status), - 3 => return MessageInfo::new("shutdown control".to_string(), decode3), - 4 => return MessageInfo::new("cell data".to_string(), decode_cell_voltages), - 160 => { - return MessageInfo::new( - "temperatures (igbt modules, gate driver board)".to_string(), - decode5, - ) - } - 161 => return MessageInfo::new("temperatures (control board)".to_string(), decode6), - 162 => return MessageInfo::new("temperatures (motor)".to_string(), decode7), - 163 => return MessageInfo::new("analog input voltages".to_string(), decode8), - 164 => return MessageInfo::new("digital input status".to_string(), decode9), - 165 => return MessageInfo::new("motor position information".to_string(), decode10), - 166 => return MessageInfo::new("Current information".to_string(), decode11), - 167 => return MessageInfo::new("Voltage Information".to_string(), decode12), - 168 => return MessageInfo::new("Flux Information".to_string(), decode13), - 169 => return MessageInfo::new("Internal Voltages".to_string(), decode14), - 170 => return MessageInfo::new("Internal States".to_string(), decode15), - 171 => return MessageInfo::new("Fault Codes".to_string(), decode16), - 172 => return MessageInfo::new("Torque and Timer Decoder".to_string(), decode17), - 192 => return MessageInfo::new("Command Data".to_string(), decode18), - 514 => return MessageInfo::new("Current Limits".to_string(), decode19), - 768 => { - return MessageInfo::new( - "NERduino Accelerometer".to_string(), - decode_accelerometer_data, - ) - } - 769 => return MessageInfo::new("NERduino Humidity".to_string(), decode21), - 7 => return MessageInfo::new("Cell Voltages".to_string(), decode_mock), - 193 => return MessageInfo::new("Unknown 1".to_string(), decode_mock), - 6 => return MessageInfo::new("Unknown 2".to_string(), decode_mock), - 194 => return MessageInfo::new("Unknown 3".to_string(), decode_mock), - 1744 => return MessageInfo::new("Unknown 4".to_string(), decode_mock), - 1745 => return MessageInfo::new("Unknown 5".to_string(), decode_mock), - 175 => return MessageInfo::new("Unknown 6".to_string(), decode_mock), - 770 => return MessageInfo::new("GLV Current".to_string(), decode29), - 2015 => return MessageInfo::new("Unknown 2015".to_string(), decode_mock), - 2027 => return MessageInfo::new("Unknown 2027".to_string(), decode_mock), - 2019 => return MessageInfo::new("Unknown 2019".to_string(), decode_mock), - 771 => return MessageInfo::new("Strain Gauge".to_string(), decode34), - 1024 => return MessageInfo::new("Wheel State".to_string(), decode35), - 10 => return MessageInfo::new("MPU States".to_string(), decode_mpu_dashboard_info), - 772 => return MessageInfo::new("GPS Data 1".to_string(), decode_gps_1), - 773 => return MessageInfo::new("GPS Data 2".to_string(), decode_gps_2), - 774 => return MessageInfo::new("GPS Data 3".to_string(), decode_gps_3), - 8 => return MessageInfo::new("Cell Temperatures".to_string(), decode_cell_temps), - 9 => return MessageInfo::new("Segment Temperatures".to_string(), decode_segment_temps), - 775 => return MessageInfo::new("Logging Status".to_string(), decode_logging_status), - 1025 => return MessageInfo::new("LV Battery 1".to_string(), decode_lv_battery_1), - 1026 => return MessageInfo::new("LV Battery 2".to_string(), decode_lv_battery_2), - _ => return MessageInfo::new("Unknown".to_string(), decode_mock), - } -} - -#[derive(Clone)] -pub struct DataInfo { - name: String, - units: String, -} - -impl DataInfo { - pub fn new(name: String, units: String) -> Self { - Self { name, units } - } -} - -// maps from data id to DataInfo containing the name of the data and its units -pub fn get_data_info(id: u8) -> DataInfo { - match id { - 0 => return DataInfo::new("Mock Data".to_string(), "".to_string()), - 1 => return DataInfo::new("Pack Inst Voltage".to_string(), "V".to_string()), - 2 => return DataInfo::new("Pack Current".to_string(), "A".to_string()), - 3 => return DataInfo::new("Pack Amphours".to_string(), "Ah".to_string()), - 4 => return DataInfo::new("Pack SOC".to_string(), "%".to_string()), - 5 => return DataInfo::new("Pack Health".to_string(), "%".to_string()), - 6 => return DataInfo::new("Failsafe Statuses".to_string(), "HEX".to_string()), - 7 => return DataInfo::new("DTC Status 1".to_string(), "HEX".to_string()), - 8 => return DataInfo::new("DTC Status 2".to_string(), "HEX".to_string()), - 9 => return DataInfo::new("Current Limits Status".to_string(), "".to_string()), - 10 => return DataInfo::new("Average Temp".to_string(), "C".to_string()), - 11 => return DataInfo::new("Internal Temp".to_string(), "C".to_string()), - 12 => return DataInfo::new("MPE State".to_string(), "BIN".to_string()), - 13 => return DataInfo::new("High Cell Voltage".to_string(), "V".to_string()), - 14 => return DataInfo::new("High Cell Voltage ID".to_string(), "".to_string()), - 15 => return DataInfo::new("Low Cell Voltage".to_string(), "V".to_string()), - 16 => return DataInfo::new("Low Cell Voltage ID".to_string(), "".to_string()), - 17 => return DataInfo::new("Average Cell Voltage".to_string(), "V".to_string()), - 18 => return DataInfo::new("Module A Temperature".to_string(), "C".to_string()), - 19 => return DataInfo::new("Module B Temperature".to_string(), "C".to_string()), - 20 => return DataInfo::new("Module C Temperature".to_string(), "C".to_string()), - 21 => return DataInfo::new("Gate Driver Board Temperature".to_string(), "C".to_string()), - 22 => return DataInfo::new("Control Board Temperature".to_string(), "C".to_string()), - 23 => return DataInfo::new("RTD #1 Temperature".to_string(), "C".to_string()), - 24 => return DataInfo::new("RTD #2 Temperature".to_string(), "C".to_string()), - 25 => return DataInfo::new("RTD #3 Temperature".to_string(), "C".to_string()), - 26 => return DataInfo::new("RTD #4 Temperature".to_string(), "C".to_string()), - 27 => return DataInfo::new("RTD #5 Temperature".to_string(), "C".to_string()), - 28 => return DataInfo::new("Motor Temperature".to_string(), "C".to_string()), - 29 => return DataInfo::new("Torque Shudder".to_string(), "N-m".to_string()), - 30 => return DataInfo::new("Analog Input 1".to_string(), "V".to_string()), - 31 => return DataInfo::new("Analog Input 2".to_string(), "V".to_string()), - 32 => return DataInfo::new("Analog Input 3".to_string(), "V".to_string()), - 33 => return DataInfo::new("Analog Input 4".to_string(), "V".to_string()), - 34 => return DataInfo::new("Analog Input 5".to_string(), "V".to_string()), - 35 => return DataInfo::new("Analog Input 6".to_string(), "V".to_string()), - 36 => return DataInfo::new("Digital Input 1".to_string(), "BIN".to_string()), - 37 => return DataInfo::new("Digital Input 2".to_string(), "BIN".to_string()), - 38 => return DataInfo::new("Digital Input 3".to_string(), "BIN".to_string()), - 39 => return DataInfo::new("Digital Input 4".to_string(), "BIN".to_string()), - 40 => return DataInfo::new("Digital Input 5".to_string(), "BIN".to_string()), - 41 => return DataInfo::new("Digital Input 6".to_string(), "BIN".to_string()), - 42 => return DataInfo::new("Digital Input 7".to_string(), "BIN".to_string()), - 43 => return DataInfo::new("Digital Input 8".to_string(), "BIN".to_string()), - 44 => return DataInfo::new("Motor Angle Electrical".to_string(), "Deg".to_string()), - 45 => return DataInfo::new("Motor Speed".to_string(), "RPM".to_string()), - 46 => return DataInfo::new("Electrical Output Frequency".to_string(), "Hz".to_string()), - 48 => return DataInfo::new("Phase A Current".to_string(), "A".to_string()), - 49 => return DataInfo::new("Phase B Current".to_string(), "A".to_string()), - 50 => return DataInfo::new("Phase C Current".to_string(), "A".to_string()), - 51 => return DataInfo::new("DC Bus Current".to_string(), "A".to_string()), - 52 => return DataInfo::new("DC Bus Voltage".to_string(), "V".to_string()), - 53 => return DataInfo::new("Output Voltage".to_string(), "V".to_string()), - 54 => return DataInfo::new("VAB_Vd Voltage".to_string(), "V".to_string()), - 55 => return DataInfo::new("VBC_Vq Voltage".to_string(), "V".to_string()), - 56 => return DataInfo::new("Flux Command".to_string(), "Wb".to_string()), - 57 => return DataInfo::new("Flux Feedback".to_string(), "wb".to_string()), - 58 => return DataInfo::new("Id Feedback".to_string(), "A".to_string()), - 59 => return DataInfo::new("Iq Feedback".to_string(), "A".to_string()), - 60 => return DataInfo::new("1.5V Reference Voltage".to_string(), "V".to_string()), - 61 => return DataInfo::new("2.5V Reference Voltage".to_string(), "V".to_string()), - 62 => return DataInfo::new("5.0V Reference Voltage".to_string(), "V".to_string()), - 63 => return DataInfo::new("12V System Voltage".to_string(), "V".to_string()), - 64 => return DataInfo::new("VSM State".to_string(), "".to_string()), - 65 => return DataInfo::new("Inverter State".to_string(), "".to_string()), - 66 => return DataInfo::new("Relay State".to_string(), "BIN".to_string()), - 67 => return DataInfo::new("Inverter Run Mode".to_string(), "BIN".to_string()), - 68 => { - return DataInfo::new( - "Inverter Active Discharge State".to_string(), - "BIN".to_string(), - ) - } - 69 => return DataInfo::new("Inverter Command Mode".to_string(), "BIN".to_string()), - 70 => return DataInfo::new("Inverter Enable State".to_string(), "BIN".to_string()), - 71 => return DataInfo::new("Inverter Enable Lockout".to_string(), "BIN".to_string()), - 72 => return DataInfo::new("Direction Command".to_string(), "BIN".to_string()), - 73 => return DataInfo::new("BMS Active".to_string(), "BIN".to_string()), - 74 => return DataInfo::new("BMS Limiting Torque".to_string(), "BIN".to_string()), - 75 => return DataInfo::new("POST Fault Lo".to_string(), "BIN".to_string()), - 76 => return DataInfo::new("POST Fault Hi".to_string(), "BIN".to_string()), - 77 => return DataInfo::new("Run Fault Lo".to_string(), "BIN".to_string()), - 78 => return DataInfo::new("Run Fault Hi".to_string(), "BIN".to_string()), - 79 => return DataInfo::new("Commanded Torque".to_string(), "N-m".to_string()), - 80 => return DataInfo::new("Torque Feedback".to_string(), "N-m".to_string()), - 81 => return DataInfo::new("Power on Timer".to_string(), "s".to_string()), - 82 => return DataInfo::new("Torque Command".to_string(), "N-m".to_string()), - 83 => return DataInfo::new("Speed Command".to_string(), "RPM".to_string()), - 84 => return DataInfo::new("Direction Command".to_string(), "BIN".to_string()), - 85 => return DataInfo::new("Inverter Enable".to_string(), "BIN".to_string()), - 86 => return DataInfo::new("Inverter Discharge".to_string(), "BIN".to_string()), - 87 => return DataInfo::new("Speed Mode Enable".to_string(), "BIN".to_string()), - 88 => return DataInfo::new("Commanded Torque Limit".to_string(), "N-m".to_string()), - 89 => return DataInfo::new("Pack DCL".to_string(), "A".to_string()), - 90 => return DataInfo::new("Pack CCL".to_string(), "A".to_string()), - 91 => return DataInfo::new("TCU X-Axis Acceleration".to_string(), "g".to_string()), - 92 => return DataInfo::new("TCU Y-Axis Acceleration".to_string(), "g".to_string()), - 93 => return DataInfo::new("TCU Z-Axis Acceleration".to_string(), "g".to_string()), - 94 => return DataInfo::new("TCU Temperature C".to_string(), "C".to_string()), - 95 => return DataInfo::new("TCU Temperature F".to_string(), "F".to_string()), - 96 => return DataInfo::new("Relative Humidity".to_string(), "%".to_string()), - 97 => return DataInfo::new("Cell Voltage Info".to_string(), "".to_string()), - 98 => return DataInfo::new("GLV Current".to_string(), "A".to_string()), - 99 => return DataInfo::new("Strain Gauge Voltage 1".to_string(), "V".to_string()), - 100 => return DataInfo::new("Strain Gauge Voltage 2".to_string(), "V".to_string()), - 101 => return DataInfo::new("Vehicle Speed".to_string(), "MPH".to_string()), - 102 => return DataInfo::new("Wheel Knob 1".to_string(), "".to_string()), - 103 => return DataInfo::new("Wheel Knob 2".to_string(), "".to_string()), - 104 => return DataInfo::new("Wheel Buttons".to_string(), "".to_string()), - 105 => return DataInfo::new("MPU Mode State".to_string(), "".to_string()), - 106 => return DataInfo::new("BMS State".to_string(), "".to_string()), - 107 => return DataInfo::new("BMS Faults".to_string(), "HEX".to_string()), - 108 => return DataInfo::new("Latitude".to_string(), "Deg".to_string()), - 109 => return DataInfo::new("Longitude".to_string(), "Deg".to_string()), - 110 => return DataInfo::new("GPS Fix Status".to_string(), "".to_string()), - 111 => return DataInfo::new("Altitude".to_string(), "m".to_string()), - 112 => return DataInfo::new("Ground Speed".to_string(), "m/s".to_string()), - 113 => return DataInfo::new("Heading Direction".to_string(), "Deg".to_string()), - 114 => return DataInfo::new("High Cell Temp".to_string(), "C".to_string()), - 115 => return DataInfo::new("High Cell Temp Chip Number".to_string(), "".to_string()), - 116 => return DataInfo::new("High Cell Temp Cell Number".to_string(), "".to_string()), - 117 => return DataInfo::new("Low Cell Temp".to_string(), "C".to_string()), - 118 => return DataInfo::new("Low Cell Temp Chip Number".to_string(), "".to_string()), - 119 => return DataInfo::new("Low Cell temp Cell Number".to_string(), "".to_string()), - 120 => return DataInfo::new("Average Cell Temp".to_string(), "C".to_string()), - 121 => return DataInfo::new("High Cell Voltage Chip Number".to_string(), "".to_string()), - 122 => return DataInfo::new("High Cell Voltage Cell Number".to_string(), "".to_string()), - 123 => return DataInfo::new("Low Cell Voltage Chip Number".to_string(), "".to_string()), - 124 => return DataInfo::new("Low Cell Voltage Cell Number".to_string(), "".to_string()), - 125 => return DataInfo::new("Segment 1 Average Temperature".to_string(), "C".to_string()), - 126 => return DataInfo::new("Segment 2 Average Temperature".to_string(), "C".to_string()), - 127 => return DataInfo::new("Segment 3 Average Temperature".to_string(), "C".to_string()), - 128 => return DataInfo::new("Segment 4 Average Temperature".to_string(), "C".to_string()), - 129 => return DataInfo::new("Logging Status".to_string(), "".to_string()), - 130 => return DataInfo::new("Accumulator Fan Percentage".to_string(), "%".to_string()), - 131 => return DataInfo::new("Motor Fan Percentage".to_string(), "%".to_string()), - 132 => return DataInfo::new("Torque Limit Percentage".to_string(), "%".to_string()), - 133 => return DataInfo::new("Regen Strength value".to_string(), "".to_string()), - 134 => return DataInfo::new("Carger State".to_string(), "".to_string()), - 135 => return DataInfo::new("Measurement System Valid".to_string(), "".to_string()), - 136 => return DataInfo::new("System Status".to_string(), "".to_string()), - 137 => return DataInfo::new("Charge Status".to_string(), "".to_string()), - 138 => return DataInfo::new("ibat".to_string(), "A".to_string()), - 139 => return DataInfo::new("vbat".to_string(), "V".to_string()), - 140 => return DataInfo::new("vin".to_string(), "V".to_string()), - 141 => return DataInfo::new("vsys".to_string(), "V".to_string()), - 142 => return DataInfo::new("iin".to_string(), "A".to_string()), - 143 => return DataInfo::new("Cell Burning Status".to_string(), "".to_string()), - 144 => return DataInfo::new("Traction Control On".to_string(), "".to_string()), - 145 => return DataInfo::new("Precharge State".to_string(), "".to_string()), - 146 => return DataInfo::new("BMS Prefault Status".to_string(), "".to_string()), - _ => return DataInfo::new("".to_string(), "".to_string()), +pub fn get_message_info(id: &u32) -> MessageInfo { + match id { 0x80 => MessageInfo::new(decode_accumulator_status), + 0x81 => MessageInfo::new(decode_bms_status), + 0x82 => MessageInfo::new(decode_shutdown_control), + 0x83 => MessageInfo::new(decode_cell_data), + 0x84 => MessageInfo::new(decode_cell_temperatures), + 0x85 => MessageInfo::new(decode_segment_temperatures), + 0x500 => MessageInfo::new(decode_nerduino_acceleromter), + 0x501 => MessageInfo::new(decode_mpu_status), + 0x680 => MessageInfo::new(decode_wheel_state), + _ => MessageInfo::new(decode_mock), } -} +} \ No newline at end of file diff --git a/src/message.rs b/src/message.rs index ecbfdb4..0a8c9be 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,41 +1,50 @@ - -use chrono::prelude::*; - use super::data::Data; -use super::master_mapping::get_message_info; -pub struct Message<'a> { - // Wrapper class for an individual message. - timestamp: DateTime, +use super::master_mapping::get_message_info; +/** + * Wrapper class for an individual message. + */ +pub struct Message { id: u32, - data: &'a [u8], + data: Vec, } -impl<'a> Message<'a> { - pub fn new(timestamp: &DateTime, id: &u32, data: &'a [u8]) -> Self { +/** + * Implementation of Message. + */ +impl Message { + /** + * Creates a new message with the given timestamp, id, and data. + */ + pub fn new(id: u32, data: Vec) -> Self { Self { - timestamp: *timestamp, - id: *id, + id, data, } } + + /** + * Decodes the message and returns a vector of Data objects. + */ pub fn decode(&self) -> Vec { - self.decode_message(&self.timestamp, &self.id, &self.data) + Message::decode_message(&self.id, &self.data) } - fn decode_message( - &self, - timestamp: &DateTime, - id: &u32, - data: &[u8], - ) -> Vec { + /** + * Decodes the message and returns a vector of Data objects. + * Achieves this by calling the decoder function associated with the message id. + * param timestamp: The timestamp of the message. + * param id: The id of the message. + * param data: The data of the message. + * return: A vector of Data objects. + */ + fn decode_message(id: &u32, data: &[u8]) -> Vec { let decoder = get_message_info(id).decoder; - println!("ATTEMPTING TO CUCK: {}", id); let mut decoded_data: Vec = Vec::new(); let result = decoder(data); - for (data_id, value) in result { - decoded_data.push(Data::new(*timestamp, data_id, value)); + for data in result { + decoded_data.push(data); } - return decoded_data; + decoded_data } } diff --git a/src/mqtt.rs b/src/mqtt.rs new file mode 100644 index 0000000..393fe69 --- /dev/null +++ b/src/mqtt.rs @@ -0,0 +1,157 @@ +extern crate paho_mqtt as mqtt; +use mqtt::ServerResponse; +use std::time::Duration; +use std::{process, thread}; + +use crate::client::Client; +use crate::data::Data; + +pub const DFLT_BROKER: &str = "mqtt://localhost:1883"; +const DFLT_CLIENT: &str = "calypso"; + +/** + * MqttClient is a wrapper around the paho_mqtt::Client. + */ +pub struct MqttClient { + client: Option, +} + +/** + * Implement the Publish trait for MqttClient. + */ +impl Client for MqttClient { + /** + * Publishes the given data to the broker. + * param data: The data object to format and send. + */ + fn publish(&mut self, data: &Data) { + let topic = "/Calypso".to_string(); + let payload = data.to_json(); + + /* If the client is initialized, publish the data. */ + if let Some(client) = &self.client { + let msg = mqtt::MessageBuilder::new() + .topic(topic) + .payload(payload) + .finalize(); + + match { client.publish(msg) } { + Ok(_) => (), + Err(e) => println!("Error sending message: {:?}", e), + } + thread::sleep(Duration::from_millis(1)); + } else { + println!("Client not initialized, please set host first and connect") + } + } + + /** + * Connects to the broker. + * Sets the host and then connects + */ + fn connect(&mut self, host: &str) { + self.set_host(&host.to_string()); + self.connect(); + } +} + +impl Default for MqttClient { + /** + * Creates a new MqttClient. + */ + fn default() -> Self { + Self::new() + } +} + +/** + * Implementation of the MqttClient struct. + */ +impl MqttClient { + /** + * Creates a new MqttClient. + */ + pub fn new() -> MqttClient { + MqttClient { client: None } + } + + /** + * Creates a new MqttClient with the given host name. + * param host_name: The host name of the broker. + */ + fn set_host(&mut self, host_name: &String) { + let create_options = mqtt::CreateOptionsBuilder::new() + .server_uri(host_name) + .client_id(DFLT_CLIENT.to_string()) + .finalize(); + self.client = Some(match { mqtt::Client::new(create_options) } { + Ok(client) => client, + Err(e) => { + println!("Error creating the client: {:?}", e); + process::exit(1); + } + }); + } + + /** + * Connects to the broker. + * Sets the last will and testament. + */ + fn connect(&mut self) { + if let Some(client) = &self.client { + let lastwilltestatment = mqtt::MessageBuilder::new() + .topic("Calypso/Status") + .payload("Calypso is offline") + .finalize(); + let conn_opts = mqtt::ConnectOptionsBuilder::new() + .keep_alive_interval(Duration::from_secs(20)) + .clean_session(false) + .will_message(lastwilltestatment) + .finalize(); + if let Err(e) = client.connect(conn_opts) { + println!("Unable to connect:\n\t{:?}", e); + process::exit(1); + } + } else { + println!("Client not initialized, please set host first"); + } + } + + /** + * Check if the client is connected to the broker. + */ + fn _is_connected(&self) -> bool { + if let Some(client) = &self.client { + client.is_connected() + } else { + println!("Client not initialized, please set host first"); + false + } + } + + /** + * Reconnect to the broker. + */ + fn _reconnect(&mut self) -> Result { + if let Some(client) = &self.client { + client.reconnect() + } else { + Err(mqtt::Error::General( + "Client not initialized, please set host first", + )) + } + } + + /** + * Disconnect from the broker. + */ + fn _disconnect(&mut self) -> Result<(), mqtt::Error> { + if let Some(client) = &self.client { + client.disconnect(None) + } else { + Err(mqtt::Error::General( + "Client not initialized, please set host first", + )) + } + } +} diff --git a/src/telem_main.rs b/src/telem_main.rs deleted file mode 100644 index 3d3de3d..0000000 --- a/src/telem_main.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::fs::File; -use std::io::BufRead; -use std::io::BufReader; -use std::io::Write; -use rayon::prelude::*; -use chrono::prelude::*; - -use master_mapping::DATA_IDS; -use master_mapping::MESSAGE_IDS; - -mod master_mapping; - -const DEFAULT_LOGS_DIRECTORY: &str = "./logs/"; -const DEFAULT_OUTPUT_PATH: &str = "./output/"; -const PROCESSORS: u8 = 8; -const PROCESS_CHUNK_SIZE: u32 = 85000; //Processes data in chunks, specified by this variable - -fn get_line_count(filepaths: &[String]) -> usize { - /* - * Gets the total line count of all the files in the list. - - There is no native way to get line counts of files without looping, so - this function gets the total size and estimates the line count based - on a subset of N lines. - */ - if filepaths.is_empty() { - return 0; - } - - const n: u8 = 20; - let tested_lines: u8 = 0; - let tested_size: u8 = 0; - let total_size: u64 = filepaths.iter().map(|filepath| fs::metadata(filepath).unwrap().len()).sum(); - - for filepath in filepaths { - let file = File::open(filepath).unwrap(); - let reader = BufReader::new(file); - - for line in reader.lines() { - let line = line.unwrap(); - total_size += line.len(); - tested_lines += 1; - - if tested_lines >= n { - return (total_size / tested_lines) * (total_size / tested_lines); - } - } - } - return total_size / (tested_size / tested_lines) -} - -fn find_time(start: &[&str], finish: &[&str]) { - /* - * Prints the difference between the two times provided - Both inputs are lists of strings: - - minutes being the zeroth index of the list - - seconds being the first index of the list - - microseconds being the second index of the list - */ - - let start_minutes = start[0].parse::().unwrap(); - let start_seconds = start[1].parse::().unwrap(); - let start_microseconds = start[2].parse::().unwrap(); - - let finish_minutes = finish[0].parse::().unwrap(); - let finish_seconds = finish[1].parse::().unwrap(); - let finish_microseconds = finish[2].parse::().unwrap(); - - let mut minutes = finish_minutes - start_minutes; - let mut seconds = finish_seconds - start_seconds; - let mut microseconds = finish_microseconds - start_microseconds; - - if microseconds < 0 { - seconds -= 1; - microseconds += 1_000_000; - } - - if seconds < 0 { - minutes -= 1; - seconds += 60; - } - - println!("Time to process (Minutes:Seconds.Microseconds): {}:{}.{:06}", minutes, seconds, microseconds); -} - -fn process_lines(lines: &mut Vec, writer: &mut csv::Writer<&mut dyn Write>) { - /* - * Processes a chunk of lines and writes the results to the CSV. - */ - let out: Vec> = lines.par_iter().map(|line| thread(line)).collect(); - lines.clear(); - for data in out { - for sub_data in data { - let str_time = sub_data.timestamp.format("%Y-%m-%dT%H:%M:%S.%fZ").to_string(); - writer.write_record(&[&str_time, &sub_data.id.to_string(), &DATA_IDS[sub_data.id]["name"], &sub_data.value.to_string()]).unwrap(); - } - } -} diff --git a/src/thread.rs b/src/thread.rs deleted file mode 100644 index c2e2362..0000000 --- a/src/thread.rs +++ /dev/null @@ -1,9 +0,0 @@ -use decode_files::{LogFormat, processLine}; -use message::Message; - -const FORMAT: LogFormat = LogFormat::Textual1; - -fn thread(line: &str) -> Result, MessageFormatException> { - let message = processLine(line, FORMAT)?; - message.decode() -} \ No newline at end of file