From d449e3480b40f8f851c11a98624c83bb11b1e3e2 Mon Sep 17 00:00:00 2001 From: Ethan Beauclaire Date: Fri, 13 Mar 2026 15:38:58 -0500 Subject: [PATCH] pollinator: add weather sensors to query, using rwis_api to fetch/insert data --- Cargo.lock | 854 +++++++++++++++++++++++-- etc/iris-server.properties | 13 + pollinator/Cargo.toml | 3 + pollinator/src/comm_link.rs | 69 +- pollinator/src/lib.rs | 1 + pollinator/src/rwis_api/api_utility.rs | 168 +++++ pollinator/src/rwis_api/mod.rs | 200 ++++++ 7 files changed, 1240 insertions(+), 68 deletions(-) create mode 100644 pollinator/src/rwis_api/api_utility.rs create mode 100644 pollinator/src/rwis_api/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 41abcc9e8e..fae03b225d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,10 +120,10 @@ dependencies = [ "bytes", "form_urlencoded", "futures-util", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.8.1", "hyper-util", "itoa", "matchit", @@ -135,7 +135,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tower", "tower-layer", @@ -151,12 +151,12 @@ checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", - "sync_wrapper", + "sync_wrapper 1.0.2", "tower-layer", "tower-service", "tracing", @@ -174,8 +174,8 @@ dependencies = [ "futures-core", "futures-util", "headers", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", @@ -184,6 +184,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -222,6 +228,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.11.0" @@ -241,7 +253,7 @@ dependencies = [ name = "bulb" version = "5.82.0" dependencies = [ - "base64", + "base64 0.22.1", "chrono", "cidr", "console_error_panic_hook", @@ -365,6 +377,26 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -465,12 +497,32 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dunce" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "enumflags2" version = "0.7.12" @@ -516,6 +568,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "event-listener" version = "5.4.1" @@ -543,6 +605,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -577,6 +645,21 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -664,9 +747,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "slab", ] @@ -727,6 +812,25 @@ dependencies = [ "pix", ] +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.13" @@ -738,7 +842,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.4.0", "indexmap", "slab", "tokio", @@ -773,10 +877,10 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "headers-core", - "http", + "http 1.4.0", "httpdate", "mime", "sha1", @@ -788,7 +892,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http", + "http 1.4.0", ] [[package]] @@ -821,7 +925,7 @@ dependencies = [ "futures", "headers", "heck", - "http", + "http 1.4.0", "loam", "log", "mvt", @@ -848,6 +952,17 @@ dependencies = [ "whoami", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.4.0" @@ -858,6 +973,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -865,7 +991,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.4.0", ] [[package]] @@ -876,8 +1002,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -899,6 +1025,30 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.8.1" @@ -909,9 +1059,9 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", - "http", - "http-body", + "h2 0.4.13", + "http 1.4.0", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -922,6 +1072,19 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.32", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -929,9 +1092,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "bytes", - "http", - "http-body", - "hyper", + "http 1.4.0", + "http-body 1.0.1", + "hyper 1.8.1", "pin-project-lite", "tokio", "tower-service", @@ -961,12 +1124,114 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "id-arena" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -979,6 +1244,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + [[package]] name = "itoa" version = "1.0.17" @@ -1067,6 +1338,18 @@ dependencies = [ "libc", ] +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + [[package]] name = "loam" version = "0.5.0" @@ -1114,7 +1397,7 @@ dependencies = [ "argh", "axum", "env_logger", - "http", + "http 1.4.0", "log", "resin", "serde", @@ -1210,6 +1493,23 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ntcip" version = "0.14.4" @@ -1246,7 +1546,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags", + "bitflags 2.11.0", ] [[package]] @@ -1264,6 +1564,60 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-src" +version = "300.5.5+3.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking" version = "2.2.1" @@ -1336,6 +1690,12 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a054a84d1ff0dc456386e5fc081e099e6855ddcc8913dc9349c294d47d76bd4" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "pointy" version = "0.7.0" @@ -1357,10 +1717,13 @@ dependencies = [ "env_logger", "futures-util", "http-body-util", - "hyper", + "hyper 1.8.1", "hyper-util", "jiff", "log", + "openssl", + "postgres", + "reqwest", "resin", "serde", "serde_json", @@ -1388,13 +1751,27 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "postgres" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c48ece1c6cda0db61b058c1721378da76855140e9214339fa1317decacb176" +dependencies = [ + "bytes", + "fallible-iterator", + "futures-util", + "log", + "tokio", + "tokio-postgres", +] + [[package]] name = "postgres-protocol" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ee9dd5fe15055d2b6806f4736aa0c9637217074e224bbec46d4041b91bb9491" dependencies = [ - "base64", + "base64 0.22.1", "byteorder", "bytes", "fallible-iterator", @@ -1416,6 +1793,17 @@ dependencies = [ "cidr", "fallible-iterator", "postgres-protocol", + "serde_core", + "serde_json", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", ] [[package]] @@ -1558,7 +1946,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.11.0", ] [[package]] @@ -1575,13 +1963,53 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "resin" version = "5.82.0" dependencies = [ "bb8", "bb8-postgres", - "hyper", + "hyper 1.8.1", "jiff", "log", "mag", @@ -1626,6 +2054,19 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + [[package]] name = "rustls" version = "0.23.37" @@ -1642,6 +2083,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + [[package]] name = "rustls-pki-types" version = "1.14.0" @@ -1675,12 +2125,44 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.27" @@ -1816,6 +2298,16 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.3" @@ -1826,6 +2318,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "stringprep" version = "0.1.5" @@ -1854,18 +2352,69 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tagptr" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "tfon" version = "0.2.0" @@ -1946,6 +2495,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.10.0" @@ -1972,7 +2531,7 @@ dependencies = [ "mio", "parking_lot", "pin-project-lite", - "socket2", + "socket2 0.6.3", "tokio-macros", "windows-sys 0.61.2", ] @@ -1988,6 +2547,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-postgres" version = "0.7.16" @@ -2008,7 +2577,7 @@ dependencies = [ "postgres-protocol", "postgres-types", "rand 0.9.2", - "socket2", + "socket2 0.6.3", "tokio", "tokio-util", "whoami", @@ -2069,7 +2638,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tower-layer", "tower-service", @@ -2085,7 +2654,7 @@ dependencies = [ "axum-core", "cookie", "futures-util", - "http", + "http 1.4.0", "parking_lot", "pin-project-lite", "tower-layer", @@ -2111,7 +2680,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a05911f23e8fae446005fe9b7b97e66d95b6db589dc1c4d59f6a2d4d4927d3" dependencies = [ "async-trait", - "http", + "http 1.4.0", "time", "tokio", "tower-cookies", @@ -2129,9 +2698,9 @@ checksum = "ce8cce604865576b7751b7a6bc3058f754569a60d689328bb74c52b1d87e355b" dependencies = [ "async-trait", "axum-core", - "base64", + "base64 0.22.1", "futures", - "http", + "http 1.4.0", "parking_lot", "rand 0.8.5", "serde", @@ -2200,7 +2769,7 @@ checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", - "http", + "http 1.4.0", "httparse", "log", "rand 0.9.2", @@ -2260,7 +2829,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc" dependencies = [ - "base64", + "base64 0.22.1", "flate2", "log", "percent-encoding", @@ -2277,18 +2846,36 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" dependencies = [ - "base64", - "http", + "base64 0.22.1", + "http 1.4.0", "httparse", "log", ] +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "1.22.0" @@ -2300,6 +2887,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -2455,7 +3048,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags", + "bitflags 2.11.0", "hashbrown 0.15.5", "indexmap", "semver", @@ -2552,13 +3145,22 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2570,34 +3172,67 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2610,30 +3245,64 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -2692,7 +3361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags", + "bitflags 2.11.0", "indexmap", "log", "serde", @@ -2722,6 +3391,35 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.41" @@ -2742,12 +3440,66 @@ dependencies = [ "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zip" version = "0.6.6" diff --git a/etc/iris-server.properties b/etc/iris-server.properties index d8252538b5..24db3f2e09 100644 --- a/etc/iris-server.properties +++ b/etc/iris-server.properties @@ -50,3 +50,16 @@ keystore.file=/etc/iris/iris-server.keystore #keystore.password=password # Gate arm allowlist (CIDR) #gate.arm.allowlist=192.168.0.0/16 10.0.0.0/8 +# +# **************************************************************************** +# +# CampbellCloud API properties +# +# CampbellCloud API host +#campbellcloud.host=https://apiserver.campbell-cloud.com +# Organization ID for CampbellCloud API +#campbellcloud.org_id=12345678-9abc-def0... +# Email/username for API authentication +#campbellcloud.user=user@email.com +# Password for API authentication +#campbellcloud.pass=SOMEpass!@#$ diff --git a/pollinator/Cargo.toml b/pollinator/Cargo.toml index b6de60354e..cc98e21491 100644 --- a/pollinator/Cargo.toml +++ b/pollinator/Cargo.toml @@ -26,6 +26,9 @@ tokio-stream.workspace = true tokio-tungstenite.workspace = true tungstenite.workspace = true whoami.workspace = true +postgres = { version = "0.19.9", features = ["with-serde_json-1"] } +openssl = { version = "0.10", features = ["vendored"] } +reqwest = { version = "0.11", features = ["json", "blocking"] } [dependencies.futures-util] version = "0.3.28" diff --git a/pollinator/src/comm_link.rs b/pollinator/src/comm_link.rs index da81a558d1..811345aa0c 100644 --- a/pollinator/src/comm_link.rs +++ b/pollinator/src/comm_link.rs @@ -12,6 +12,7 @@ // use crate::binner::DetEvent; use crate::rtms_echo; +use crate::rwis_api; use futures_util::{TryStreamExt, pin_mut}; use resin::event::VehEvent; use resin::{Database, Error, Result}; @@ -23,33 +24,51 @@ use tokio::time::{Duration, MissedTickBehavior, interval}; /// SQL query for comm links const QUERY: &str = r#" SELECT row_to_json(row)::text FROM ( - SELECT l.name, protocol, uri, poll_period_sec AS per_s, - long_poll_period_sec AS long_per_s, controller, - split_part(c.password, ':', 1) AS user, - split_part(c.password, ':', 2) AS password, pins, detectors - FROM iris.comm_link l - JOIN iris.comm_config cc ON cc.name = l.comm_config - JOIN iris.controller c ON c.comm_link = l.name - INNER JOIN ( - SELECT controller, - json_agg(pin ORDER BY pin) AS pins, - json_agg(name ORDER BY pin) AS detectors - FROM iris.detector - GROUP BY controller - ) d ON d.controller = c.name - WHERE pollinator = true AND poll_enabled = true AND condition = 1 + SELECT l.name, protocol, uri, poll_period_sec AS per_s, + long_poll_period_sec AS long_per_s, controller, + split_part(c.password, ':', 1) AS user, + split_part(c.password, ':', 2) AS password, pins, + detectors, weather_sensors, alt_ids + FROM iris.comm_link l + JOIN iris.comm_config cc + ON cc.name = l.comm_config + JOIN iris.controller c + ON c.comm_link = l.name + INNER JOIN ( + SELECT controller, + json_agg(pin ORDER BY pin) AS pins, + json_agg(COALESCE(detector_name, '') ORDER BY pin) AS detectors, + json_agg(COALESCE(weather_sensor_name, '') ORDER BY pin) AS weather_sensors, + json_agg(alt_id ORDER BY pin) AS alt_ids + FROM ( + SELECT controller, pin, name AS detector_name, NULL::text AS weather_sensor_name, NULL::text AS alt_id + FROM iris.detector + + UNION ALL + + SELECT controller, pin, name AS weather_sensor_name, NULL::text AS detector_name, alt_id + FROM iris.weather_sensor + ) io + GROUP BY controller + ) d + ON d.controller = c.name + WHERE pollinator = true + AND poll_enabled = true + AND condition = 1 ) row"#; /// Comm protocol #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] enum CommProtocol { RtmsEcho, + CampbellCloud, } impl CommProtocol { /// Get protocol from ID fn from_id(id: u32) -> Option { match id { + 26 => Some(Self::CampbellCloud), 31 => Some(Self::RtmsEcho), _ => None, } @@ -186,6 +205,14 @@ impl CommLink { &self.cfg } + /// Should log_disconnect be called if it disconnects? + pub fn disconnect_is_error(&self) -> bool { + match CommProtocol::from_id(self.cfg.protocol) { + Some(CommProtocol::CampbellCloud) => false, + _ => true + } + } + /// Get comm link name pub fn name(&self) -> &str { &self.cfg.name @@ -262,7 +289,9 @@ impl CommLink { } _ => (), } - self.log_disconnect(&db).await?; + if self.disconnect_is_error() { + self.log_disconnect(&db).await?; + } match res { Err(Error::StreamDisconnected) => (), Err(err) => log::warn!("{}: {err}", self.name()), @@ -299,6 +328,12 @@ async fn try_run_link(link: &CommLink, db: Option) -> Result<()> { let sensor = rtms_echo::Sensor::new(link.clone()); sensor.run(db).await } - _ => Err(Error::InvalidConfig("protocol")), + Some(CommProtocol::CampbellCloud) => { + tokio::task::spawn_blocking(|| { + rwis_api::run().expect("rwis_api run failed"); + }).await?; + Ok(()) + } + _ => Err(Error::InvalidConfig("protocol")) } } diff --git a/pollinator/src/lib.rs b/pollinator/src/lib.rs index 1e8732e15d..949612ce66 100644 --- a/pollinator/src/lib.rs +++ b/pollinator/src/lib.rs @@ -16,6 +16,7 @@ mod binner; mod comm_link; mod http; pub mod rtms_echo; +pub mod rwis_api; pub use binner::IntervalBinner; pub use comm_link::{CommLink, CommLinkCfg}; diff --git a/pollinator/src/rwis_api/api_utility.rs b/pollinator/src/rwis_api/api_utility.rs new file mode 100644 index 0000000000..7a9c11bf0f --- /dev/null +++ b/pollinator/src/rwis_api/api_utility.rs @@ -0,0 +1,168 @@ +use reqwest::blocking::Client; +use reqwest::Error; +use serde::{Deserialize}; +use serde_json::{Value, json}; +use std::time::{Duration, SystemTime}; + +#[derive(Deserialize)] +struct Auth { + access_token: String, + expires_in: u64, +} + +pub struct ApiUtility { + client: Client, + base_url: String, + username: String, + password: String, + auth: Auth, + auth_time: SystemTime, + organization_id: String, + assets: Option, + datastreams: Option, +} + +impl ApiUtility { + /** Request new authorization tokens from the API */ + fn get_auth(base_url: &str, u: &str, p: &str) -> Auth { + let auth_url = format!("{}/api/v1/tokens", base_url); + let raw_response = Client::new() + .post(&auth_url) + .json(&json!({ + "client_id": "cloud", + "grant_type": "password", + "username": u, + "password": p + })) + .send() + .expect("Failed to send auth request") + .text() + .expect("Failed to read response body"); + + serde_json::from_str(&raw_response) + .expect("Failed to parse auth response as JSON") + } + + /** + * Create a new ApiUtility class. + * Used to access API programmatically and update the auth if needed. + */ + pub fn new(base_url: &str, username: &str, password: &str, organization_id: &str) -> Self { + ApiUtility { + client: Client::new(), + base_url: base_url.to_string(), + username: username.to_string(), + password: password.to_string(), + auth: Self::get_auth(base_url, username, password), + auth_time: SystemTime::now(), + organization_id: organization_id.to_string(), + assets: None, + datastreams: None, + } + } + + /** Check if the auth has expired, and refresh it if so */ + fn update_auth(&mut self) { + let now = SystemTime::now(); + if now.duration_since(self.auth_time).unwrap_or_default() < Duration::from_secs(self.auth.expires_in) { + return; + } + self.auth_time = SystemTime::now(); + self.auth = Self::get_auth(&self.base_url, &self.username, &self.password); + } + + /** Send a GET request defined by the endpoint and return the result if successful */ + pub fn get_request(&mut self, endpoint: &str) -> Result { + self.update_auth(); + + let request_url = format!("{}/{}", self.base_url, endpoint); + let response = self + .client + .get(&request_url) + .bearer_auth(&self.auth.access_token) + .send()?; + + if response.status().is_success() { + let json = response.json()?; + Ok(json) + } else { + if let Err(e) = response.error_for_status_ref() { + Err(e) + } else { + Ok(json!("Couldn't parse error.")) + } + } + } + + /** Request to list-datastreams API endpoint */ + pub fn list_datastreams(&mut self) -> Result { + if let Some(ds) = &self.datastreams { + return Ok(ds.to_owned()); + } + let endpoint = format!( + "api/v1/organizations/{}/datastreams?limit={}", + self.organization_id, i32::MAX + ); + let ds = self.get_request(&endpoint)?; + self.datastreams = Some(ds.clone()); + Ok(ds) + } + + /** Request to get-datastream-datapoints-last API endpoint */ + pub fn last_datapoint(&mut self, datastream: &str) -> Result { + let endpoint = format!( + "api/v1/organizations/{}/datastreams/{}/datapoints/last", + self.organization_id, datastream + ); + self.get_request(&endpoint) + } + + /** Request to list-assets API endpoint */ + pub fn list_assets(&mut self) -> Result { + if let Some(a) = &self.assets { + return Ok(a.to_owned()); + } + let endpoint = format!( + "api/v1/organizations/{}/assets", + self.organization_id + ); + let a = self.get_request(&endpoint)?; + self.assets = Some(a.clone()); + Ok(a) + } + + /** Takes a serial number of an asset, and returns the ID for that asset */ + pub fn get_id_from_serial(&mut self, s: &str) -> Option { + if let Ok(assets) = self.list_assets() { + for a in assets.as_array()? { + if a["metadata"]["serial"] == json!(s) { + return Some(a["id"].clone()); + } + } + } + None + } + + /** + * Return the value of the last datapoint of datastream for an asset. + * Wraps last_datapoint, finding the ID by measurement name ("field") and asset. + */ + pub fn get_asset_last_datapoint_value(&mut self, asset_id: &str, datastream_name: &str) -> Result { + let datastreams = self.list_datastreams(); + let mut res = Ok(json!({})); + if let Ok(ds) = datastreams { + for d in ds.as_array().unwrap() { + if d["asset_id"] == json!(asset_id) && d["metadata"]["field"] == json!(datastream_name) { + let id = d["id"].as_str().unwrap(); + if let Ok(data) = self.last_datapoint(id) { + res = Ok(data["data"][0]["value"].clone()); + break; + } + } + } + } else { + res = datastreams + } + res + } +} diff --git a/pollinator/src/rwis_api/mod.rs b/pollinator/src/rwis_api/mod.rs new file mode 100644 index 0000000000..fdc762a9da --- /dev/null +++ b/pollinator/src/rwis_api/mod.rs @@ -0,0 +1,200 @@ +mod api_utility; + +use postgres::{Client, NoTls, Error}; +use serde_json::{json, Map, Value}; +use std::collections::HashMap; + +pub use api_utility::ApiUtility; + +pub fn run() -> Result<(), Error> { + // Read IRIS server properties for API and database credentials + let props = read_properties(); + + // Host with no trailing slash + let host = props.get("campbellcloud.host").expect("campbellcloud.host not set in properties"); + // Organization ID for use with API + let organization_id = props.get("campbellcloud.org_id").expect("campbellcloud.org_id not set in properties"); + // Credentials for account with access to the organization/API + let username = props.get("campbellcloud.user").expect("campbellcloud.user not set in properties"); + let api_password = props.get("campbellcloud.pass").expect("campbellcloud.pass not set in properties"); + let mut api_util = api_utility::ApiUtility::new(&host, &username, &api_password, &organization_id); + + // Remove protocols and just use host/database name (user/pass must be inserted) + let db_url = props["db.url"].split("//").collect::>()[1]; + let mut client = Client::connect( + format!("postgresql://{}:{}@{}", props["db.user"], props["db.password"], db_url).as_str(), + NoTls + ).unwrap(); + + // Get all the samples first + if let Ok(serials) = get_serial_numbers(&mut client) { + let mut samples = vec![]; + let mut fails = vec![]; + for s in serials { + if let Some(sample) = build_sample_json(&mut api_util, &s) { + samples.push((s, sample)); + } else { + fails.push(s); + } + } + + // Then push them at same time + if let Err(e) = insert_samples(&mut client, samples) { + panic!("{}", e); + } + match insert_fails(&mut client, fails) { + Ok(_) => return Ok(()), + Err(e) => panic!("{}", e), + } + } + Ok(()) +} + +fn read_properties() -> HashMap { + let lines : Vec = std::fs::read_to_string("/etc/iris/iris-server.properties") + .unwrap() + .lines() + .map(String::from) + .collect(); + + let mut map = HashMap::::new(); + for line in lines { + match line.split_once("=") { + Some((key, value)) => { + if !key.starts_with("#") { + map.insert(key.into(), value.into()); + } + }, + None => () + } + } + map +} + +fn get_serial_numbers(client: &mut Client) -> Result, Error> { + let mut serials = vec![]; + + for row in client.query("SELECT alt_id FROM iris._weather_sensor", &[])? { + let alt_id: Option = row.get(0); + + if let Some(id) = alt_id { + if !id.is_empty() { + serials.push(id); + } + } + } + Ok(serials) +} + +/** Build the sample JSON for one station designated by SN */ +fn build_sample_json(api: &mut ApiUtility, serial_number: &str) -> Option { + if let Some(id_val) = api.get_id_from_serial(serial_number) { + let id = id_val.as_str().unwrap_or(""); + let mut s = Map::new(); + let mut changed : bool = false; + if let Ok(dpt) = api.get_asset_last_datapoint_value(id, "DewPointTemp") { + if dpt.is_f64() { + s.insert(String::from("dew_point_temp"), dpt); + changed = true; + } else { + eprintln!("DewPointTemp for asset {serial_number} is invalid"); + } + } + if let Ok(st) = api.get_asset_last_datapoint_value(id, "SurfaceTemp") { + if st.is_f64() { + let data = json!([{"surface_temp": st}]); + s.insert(String::from("pavement_sensor"), data); + changed = true; + } else { + eprintln!("SurfaceTemp for asset {serial_number} is invalid"); + } + } + if let Ok(rh) = api.get_asset_last_datapoint_value(id, "RH") { + if rh.is_f64() { + s.insert(String::from("relative_humidity"), (rh.as_f64().unwrap() as i64).into()); + changed = true; + } else { + eprintln!("RH for asset {serial_number} is invalid"); + } + } + if let Ok(at) = api.get_asset_last_datapoint_value(id, "AirTemp") { + if at.is_f64() { + let data = json!([{"air_temp": at}]); + s.insert(String::from("temperature_sensor"), data); + changed = true; + } else { + eprintln!("AirTemp for asset {serial_number} is invalid"); + } + } + + if changed { + return Some(Value::Object(s)); + } + } + + None +} + +fn insert_samples(client: &mut Client, samples: Vec<(String, Value)>) -> Result<(), Error> { + if samples.len() > 0 { + // for updating _weather_sensor + let mut values = String::new(); + // for updating controller.fail_time + let mut serial_values = String::new(); + for (serial, sample) in samples { + values.push_str(&format!("('{}', '{}'::jsonb),", serial, sample)); + serial_values.push_str(&format!("('{}'),", serial)); + } + values.pop(); // remove trailing comma + serial_values.pop(); // remove trailing comma + + let update_ws: String = format!(" + UPDATE iris._weather_sensor AS ws + SET sample = new.sample, sample_time = current_timestamp + FROM (VALUES {}) AS new(alt_id, sample) + WHERE new.alt_id = ws.alt_id;", + values + ); + let ws_updated = client.execute(&update_ws, &[])?; + + let update_failtimes = format!(" + UPDATE iris.controller AS c + SET fail_time = NULL + FROM iris.controller_io AS cio + JOIN iris._weather_sensor AS ws ON ws.name = cio.name + JOIN (VALUES {}) AS new(alt_id) + ON new.alt_id = ws.alt_id + WHERE cio.controller = c.name;", + serial_values + ); + let fails_updated = client.execute(&update_failtimes, &[])?; + + println!("Updated sample, fail_time for {}, {} rows", ws_updated, fails_updated); + } + Ok(()) +} + +fn insert_fails(client: &mut Client, fails: Vec) -> Result<(), Error> { + if fails.len() > 0 { + let mut query: String = " + UPDATE iris.controller as c + SET fail_time=current_timestamp + FROM (VALUES ".to_owned(); + for serial in fails { + query.push_str(format!("('{}'),", serial).as_str()); + } + query.pop(); + query.push_str(" + ) AS new(alt_id) + WHERE c.name = ( + SELECT controller from iris.controller_io + WHERE name=( + SELECT name from iris._weather_sensor + WHERE alt_id=new.alt_id + ) + )"); + let rows_updated = client.execute(&query, &[])?; + println!("Updated fail_time for {} rows", rows_updated); + } + Ok(()) +}