From da612482976ccb499537bafb76204a8069bfcfb8 Mon Sep 17 00:00:00 2001 From: Alexander Baranov Date: Mon, 27 Apr 2026 23:02:53 +0300 Subject: [PATCH] homework done --- smart-house/Cargo.lock | 534 +----------------- smart-house/Cargo.toml | 3 +- smart-house/README.md | 24 +- smart-house/house/Cargo.toml | 8 - smart-house/house/src/bin/house_builder.rs | 11 - smart-house/house/src/bin/mocks_example.rs | 32 -- .../house/src/bin/power_socket_client.rs | 19 - .../house/src/bin/power_socket_mock.rs | 88 --- smart-house/house/src/bin/reporter.rs | 17 - smart-house/house/src/bin/stubs_example.rs | 147 ----- smart-house/house/src/bin/subscribers.rs | 22 - smart-house/house/src/bin/thermometer_mock.rs | 53 -- smart-house/house/src/builders.rs | 53 -- smart-house/house/src/device.rs | 72 --- smart-house/house/src/error.rs | 23 - smart-house/house/src/house.rs | 154 ----- smart-house/house/src/lib.rs | 22 - smart-house/house/src/main.rs | 1 - smart-house/house/src/power_socket.rs | 177 ------ smart-house/house/src/print_status.rs | 9 - smart-house/house/src/reporter.rs | 30 - smart-house/house/src/room.rs | 126 ----- smart-house/house/src/subscriber.rs | 14 - smart-house/house/src/thermometer.rs | 121 ---- smart-house/power_socket_lib/Cargo.toml | 9 + smart-house/power_socket_lib/src/lib.rs | 45 ++ smart-house/thermometer_mock.cfg | 2 - smart-house/use_dynamic/Cargo.toml | 7 + smart-house/use_dynamic/src/main.rs | 51 ++ smart-house/use_static/Cargo.toml | 6 + smart-house/use_static/build.rs | 4 + smart-house/use_static/src/main.rs | 34 ++ 32 files changed, 187 insertions(+), 1731 deletions(-) delete mode 100644 smart-house/house/Cargo.toml delete mode 100644 smart-house/house/src/bin/house_builder.rs delete mode 100644 smart-house/house/src/bin/mocks_example.rs delete mode 100644 smart-house/house/src/bin/power_socket_client.rs delete mode 100644 smart-house/house/src/bin/power_socket_mock.rs delete mode 100644 smart-house/house/src/bin/reporter.rs delete mode 100644 smart-house/house/src/bin/stubs_example.rs delete mode 100644 smart-house/house/src/bin/subscribers.rs delete mode 100644 smart-house/house/src/bin/thermometer_mock.rs delete mode 100644 smart-house/house/src/builders.rs delete mode 100644 smart-house/house/src/device.rs delete mode 100644 smart-house/house/src/error.rs delete mode 100644 smart-house/house/src/house.rs delete mode 100644 smart-house/house/src/lib.rs delete mode 100644 smart-house/house/src/main.rs delete mode 100644 smart-house/house/src/power_socket.rs delete mode 100644 smart-house/house/src/print_status.rs delete mode 100644 smart-house/house/src/reporter.rs delete mode 100644 smart-house/house/src/room.rs delete mode 100644 smart-house/house/src/subscriber.rs delete mode 100644 smart-house/house/src/thermometer.rs create mode 100644 smart-house/power_socket_lib/Cargo.toml create mode 100644 smart-house/power_socket_lib/src/lib.rs delete mode 100644 smart-house/thermometer_mock.cfg create mode 100644 smart-house/use_dynamic/Cargo.toml create mode 100644 smart-house/use_dynamic/src/main.rs create mode 100644 smart-house/use_static/Cargo.toml create mode 100644 smart-house/use_static/build.rs create mode 100644 smart-house/use_static/src/main.rs diff --git a/smart-house/Cargo.lock b/smart-house/Cargo.lock index becae27..75ecd56 100644 --- a/smart-house/Cargo.lock +++ b/smart-house/Cargo.lock @@ -2,24 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "bitflags" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" - -[[package]] -name = "bytes" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" - [[package]] name = "cfg-if" version = "1.0.4" @@ -27,528 +9,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "chacha20" -version = "0.10.0" +name = "libloading" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" dependencies = [ "cfg-if", - "cpufeatures", - "rand_core", + "windows-link", ] [[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "getrandom" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "rand_core", - "wasip2", - "wasip3", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "indexmap" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", - "serde", - "serde_core", -] - -[[package]] -name = "itoa" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" - -[[package]] -name = "leb128fmt" +name = "power_socket_lib" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] -name = "libc" -version = "0.2.178" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "mio" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +name = "use_dynamic" +version = "0.1.0" dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", + "libloading", ] [[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" -dependencies = [ - "chacha20", - "getrandom", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "smart-house" -version = "0.0.0" -dependencies = [ - "rand", - "tokio", -] - -[[package]] -name = "socket2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tokio" -version = "1.49.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "windows-sys 0.61.2", -] - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] +name = "use_static" +version = "0.1.0" [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/smart-house/Cargo.toml b/smart-house/Cargo.toml index acf1eb6..9cbedc7 100644 --- a/smart-house/Cargo.toml +++ b/smart-house/Cargo.toml @@ -1,5 +1,6 @@ [workspace] resolver = "3" members = [ - "house" + "power_socket_lib", "use_dynamic", + "use_static", ] diff --git a/smart-house/README.md b/smart-house/README.md index 788593d..68c5c5d 100644 --- a/smart-house/README.md +++ b/smart-house/README.md @@ -228,14 +228,14 @@ Реализовать workspace со следующими package-ами: -- [ ] Библиотека умной розетки с Си ABI. -- [ ] Приложение, использующее библиотеку умной розетки, линкуя её статически. -- [ ] Приложение, использующее библиотеку умной розетки, линкуя её динамически в runtime. +- [x] Библиотека умной розетки с Си ABI. +- [x] Приложение, использующее библиотеку умной розетки, линкуя её статически. +- [x] Приложение, использующее библиотеку умной розетки, линкуя её динамически в runtime. Библиотека умной розетки с Си ABI: -- [ ] Функционал не изменяется: включение/выключение + запрос мощности. -- [ ] При сборке создаёт три артефакта: +- [x] Функционал не изменяется: включение/выключение + запрос мощности. +- [x] При сборке создаёт три артефакта: 1. Rust библиотеку 2. Статическую библиотеку с Си ABI. 3. Динамическую библиотеку с Си ABI @@ -247,3 +247,17 @@ - Workspace успешно собирается. - Приложения-примеры успешно выполняются. - Команды cargo clippy, и cargo fmt --check не выводят ошибок и предупреждений. + +### Демо + +1. Собрать библиотеку: + + cargo build -p power_socket_lib + +2. Статическая линковка: + + cargo run -p use_static + +3. Динамическая линковка: + + cargo run -p use_dynamic diff --git a/smart-house/house/Cargo.toml b/smart-house/house/Cargo.toml deleted file mode 100644 index 6d4e7db..0000000 --- a/smart-house/house/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -edition = "2024" -name = "smart-house" -version = "0.0.0" - -[dependencies] -tokio = { version = "1.49.0", features = ["rt", "net", "io-util", "time"] } -rand = { version = "0.10.0", features = ["std"] } diff --git a/smart-house/house/src/bin/house_builder.rs b/smart-house/house/src/bin/house_builder.rs deleted file mode 100644 index f59bb2b..0000000 --- a/smart-house/house/src/bin/house_builder.rs +++ /dev/null @@ -1,11 +0,0 @@ -use smart_house::{HouseBuilder, PowerSocket, PrintStatus, Thermometer}; - -fn main() { - let house = HouseBuilder::new() - .add_room("Main") - .add_device("PSockA", PowerSocket::stub(12.0, false)) - .add_room("Hall") - .add_device("ThermA", Thermometer::stub(18.5)) - .build(); - house.print_status(); -} diff --git a/smart-house/house/src/bin/mocks_example.rs b/smart-house/house/src/bin/mocks_example.rs deleted file mode 100644 index 0d5a475..0000000 --- a/smart-house/house/src/bin/mocks_example.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Пример работы умного дома с имитаторами - -use smart_house::{Device, House, PowerSocket, PrintStatus, Room, Thermometer, room}; - -fn main() -> Result<(), Box> { - let mut house = create_house_demo()?; - - let dev = house.get_room_mut("Main").unwrap().get_device_mut("PSocA").unwrap(); - if let Device::PowerSocket(psoc) = dev { - psoc.set_on(!psoc.is_on().unwrap()).unwrap(); - } - - house.print_status(); - - Ok(()) -} - -fn create_house_demo() -> Result> { - println!("# Create new smart house"); - let house = House::new( - [( - "Main".to_string(), - room!( - "PSocA" => PowerSocket::connect("127.0.0.1:10001")?, - "ThermA" => Thermometer::connect("127.0.0.1:10002")?, - ), - )] - .into_iter() - .collect(), - ); - Ok(house) -} diff --git a/smart-house/house/src/bin/power_socket_client.rs b/smart-house/house/src/bin/power_socket_client.rs deleted file mode 100644 index ecfed6b..0000000 --- a/smart-house/house/src/bin/power_socket_client.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Пример подключения нескольких клиентов к розетке. Изменение состояния любым из клиентов отражается на всех. - -use smart_house::PowerSocket; -use std::time::Duration; - -fn main() -> Result<(), Box> { - let power_socket0 = PowerSocket::connect("127.0.0.1:10001")?; - let mut power_socket = PowerSocket::connect("127.0.0.1:10001")?; - println!("{}", power_socket0.display()); - println!("{}", power_socket.display()); - - std::thread::sleep(Duration::from_secs(2)); - power_socket.set_on(!power_socket.is_on().unwrap()).unwrap(); - - println!("{}", power_socket0.display()); - println!("{}", power_socket.display()); - - Ok(()) -} diff --git a/smart-house/house/src/bin/power_socket_mock.rs b/smart-house/house/src/bin/power_socket_mock.rs deleted file mode 100644 index 3c897eb..0000000 --- a/smart-house/house/src/bin/power_socket_mock.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Сервер-имитатор умной розетки - -use std::net::SocketAddr; -use std::sync::{Arc, RwLock}; -use std::time::Duration; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; - -fn parse_args() -> Result> { - let mut args = std::env::args(); - args.next(); - Ok(args.next().ok_or(std::io::Error::other("no server address parameter specified"))?.parse()?) -} - -struct RealPowerSocket { - power_rate: f32, - on: bool, -} - -const CMD_GET_ON: u8 = 1; -const CMD_TURN_ON: u8 = 2; -const CMD_TURN_OFF: u8 = 3; -const CMD_GET_POWER: u8 = 4; - -const TIMEOUT: Duration = Duration::from_secs(5); - -async fn handle_connection(mut socket: tokio::net::TcpStream, real_power_socket: Arc>) -> Result<(), std::io::Error> { - let mut buf = [0u8; 1]; - loop { - let read = tokio::time::timeout(TIMEOUT, socket.read(&mut buf)).await??; - if read == 0 { - println!("connection closed"); - return Ok(()); - } - match buf { - [CMD_GET_ON] => { - println!("handling CMD_GET_ON"); - { - let power_socket = real_power_socket.try_read().map_err(|_| std::io::Error::other("read lock failed"))?; - buf = if power_socket.on { [1u8; 1] } else { [0u8; 1] }; - } - let _ = tokio::time::timeout(TIMEOUT, socket.write(&buf)).await?; - } - [CMD_TURN_ON] => { - println!("handling CMD_TURN_ON"); - { - let mut power_socket = real_power_socket.try_write().map_err(|_| std::io::Error::other("write lock failed"))?; - power_socket.on = true; - } - let _ = tokio::time::timeout(TIMEOUT, socket.write(&buf)).await?; - } - [CMD_TURN_OFF] => { - println!("handling CMD_TURN_OFF"); - { - let mut power_socket = real_power_socket.try_write().map_err(|_| std::io::Error::other("write lock failed"))?; - power_socket.on = false; - } - let _ = tokio::time::timeout(TIMEOUT, socket.write(&buf)).await?; - } - [CMD_GET_POWER] => { - println!("handling CMD_GET_POWER"); - let data_buf: [u8; 4]; - { - let power_socket = real_power_socket.try_read().map_err(|_| std::io::Error::other("read lock failed"))?; - data_buf = power_socket.power_rate.to_le_bytes(); - } - let _ = tokio::time::timeout(TIMEOUT, socket.write(&data_buf)).await?; - } - _ => { - println!("unknown command {} - ignore it", buf[0]); - } - } - } -} - -fn main() -> Result<(), Box> { - let real_power_socket = Arc::new(RwLock::new(RealPowerSocket { power_rate: 12.0, on: false })); - let rt = tokio::runtime::Builder::new_current_thread().enable_all().build()?; - rt.block_on(async { - let listener = tokio::net::TcpListener::bind(parse_args()?).await?; - - loop { - let (socket, _) = listener.accept().await?; - println!("new connection"); - let real_power_socket = real_power_socket.clone(); - tokio::spawn(async move { handle_connection(socket, real_power_socket).await }); - } - }) -} diff --git a/smart-house/house/src/bin/reporter.rs b/smart-house/house/src/bin/reporter.rs deleted file mode 100644 index 8cb544f..0000000 --- a/smart-house/house/src/bin/reporter.rs +++ /dev/null @@ -1,17 +0,0 @@ -use smart_house::{HouseBuilder, PowerSocket, Reporter, Thermometer}; - -fn main() { - let house = HouseBuilder::new() - .add_room("Main") - .add_device("PSockB", PowerSocket::stub(12.0, false)) - .add_room("Hall") - .add_device("ThermB", Thermometer::stub(18.5)) - .build(); - - Reporter::new() - .add_reportable(house.get_room("Main").unwrap()) - .add_reportable(house.get_device("Main", "PSockB").unwrap()) - .add_reportable(house.get_room("Hall").unwrap()) - .add_reportable(house.get_device("Hall", "ThermB").unwrap()) - .report(); -} diff --git a/smart-house/house/src/bin/stubs_example.rs b/smart-house/house/src/bin/stubs_example.rs deleted file mode 100644 index b19e242..0000000 --- a/smart-house/house/src/bin/stubs_example.rs +++ /dev/null @@ -1,147 +0,0 @@ -//! Старый пример работы умного дома на заглушках - -use smart_house::{Device, House, PowerSocket, PrintStatus, Room, Thermometer, room}; - -fn main() { - let mut house = create_house_demo(); - - switch_off_power_socket_in_hall_demo(&mut house); - - add_new_room_in_house_demo(&mut house); - - add_power_socket_to_closet_room_demo(&mut house); - - remove_thermometer_from_closet_room_demo(&mut house); - - remove_closet_room_demo(&mut house); - - searching_devices_in_house_demo(&house); - - universal_printing_function_demo(&house); -} - -fn create_house_demo() -> House { - println!("# Create new smart house"); - let house = House::new( - [ - ( - "Hall".to_string(), - room!( - "PSocA" => PowerSocket::stub(9.5, true), - "ThermA" => Thermometer::stub(20.1), - ), - ), - ( - "Main".to_string(), - room!( - "PSocB" => PowerSocket::stub(11.2, true), - "ThermB" => Thermometer::stub(24.5), - "PSocC" => PowerSocket::stub(10.4, true), - ), - ), - ( - "Bedroom".to_string(), - room!( - "ThermC" => Thermometer::stub(19.3), - "PSocD" => PowerSocket::stub(12.1, true), - ), - ), - ] - .into_iter() - .collect(), - ); - house.print_status(); - house -} - -fn switch_off_power_socket_in_hall_demo(house: &mut House) { - print!("# Switching off a power socket in Hall... "); - let Device::PowerSocket(power_socket) = house.get_room_mut("Hall").unwrap().get_device_mut("PSocA").unwrap() else { - println!("FAILED!"); - return; - }; - power_socket.set_on(false).unwrap(); - println!("SUCCESS"); - house.print_status(); -} - -fn add_new_room_in_house_demo(house: &mut House) { - println!("# Add new room into house"); - house.insert_room( - "Closet", - room!( - "ThermD" => Thermometer::stub(9.5) - ), - ); - house.print_status(); -} - -fn add_power_socket_to_closet_room_demo(house: &mut House) { - println!("# Add power socket to 'Closet' room"); - house - .get_room_mut("Closet") - .unwrap() - .insert_device("PSocE", PowerSocket::stub(8.0, true).into()); - house.print_status(); -} - -fn remove_thermometer_from_closet_room_demo(house: &mut House) { - print!("# Removing thermometer from 'Closet' room... "); - let Some(_) = house.get_room_mut("Closet").unwrap().remove_device("ThermD") else { - println!("FAILED!"); - return; - }; - println!("SUCCESS"); - house.print_status(); -} - -fn remove_closet_room_demo(house: &mut House) { - print!("# Removing 'Closet' room... "); - let Some(_) = house.remove_room("Closet") else { - println!("FAILED!"); - return; - }; - println!("SUCCESS"); - house.print_status(); -} - -fn searching_devices_in_house_demo(house: &House) { - println!("# Searching dummy device in empty room"); - find_device_handling_errors_demo(house, "empty", "dummy"); - - println!("# Searching dummy device in Bedroom"); - find_device_handling_errors_demo(house, "Bedroom", "dummy"); - - println!("# Searching ThermA device in Hall room"); - find_device_handling_errors_demo(house, "Hall", "ThermA"); - - println!(); -} - -fn find_device_handling_errors_demo(house: &House, room: &str, device: &str) { - match house.get_device(room, device) { - Err(error) => { - println!("FAIL. Error: {:?}", error); - } - Ok(device) => { - print!("SUCCESS. Device found: "); - device.print_status(); - } - } -} - -fn universal_printing_function_demo(house: &House) { - println!("# Print house using universal function"); - print_status(house); - - println!("# Print Main room using universal function"); - print_status(house.get_room("Main").unwrap()); - - println!(); - println!("# Print PSocC device from Main room using universal function"); - print_status(house.get_device("Main", "PSocC").unwrap()); -} - -fn print_status(printable: &impl PrintStatus) { - printable.print_status(); -} diff --git a/smart-house/house/src/bin/subscribers.rs b/smart-house/house/src/bin/subscribers.rs deleted file mode 100644 index 680177c..0000000 --- a/smart-house/house/src/bin/subscribers.rs +++ /dev/null @@ -1,22 +0,0 @@ -use smart_house::{Device, PowerSocket, Room, Subscriber}; - -fn main() { - let mut room = Room::default(); - - room.subscribe(MySubscriber::default()); - - room.subscribe(|dev: &Device| { - println!("device added: {}", dev.display()); - }); - - room.insert_device("", PowerSocket::stub(12.0, false).into()); -} - -#[derive(Default)] -struct MySubscriber {} - -impl Subscriber for MySubscriber { - fn on_event(&mut self, dev: &Device) { - println!("DEVICE ADDED: {}", dev.display()); - } -} diff --git a/smart-house/house/src/bin/thermometer_mock.rs b/smart-house/house/src/bin/thermometer_mock.rs deleted file mode 100644 index 95186a4..0000000 --- a/smart-house/house/src/bin/thermometer_mock.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! Имитатор термометра - -use rand::prelude::*; -use std::io::Read; -use std::net::{IpAddr, SocketAddr}; -use std::str::FromStr; -use std::sync::Arc; -use std::time::Duration; - -struct Params { - addr: SocketAddr, - interval: Duration, -} - -const CONFIG_FILE: &str = "thermometer_mock.cfg"; - -fn read_parameters_from_file() -> Result { - let mut file = std::fs::File::open(CONFIG_FILE)?; - let mut content = String::new(); - file.read_to_string(&mut content)?; - let lines = content.split("\n").collect::>(); - let addr = lines - .first() - .map(|v| SocketAddr::from_str(v)) - .ok_or(std::io::Error::other("no address found in config file"))? - .map_err(std::io::Error::other)?; - let interval = lines - .get(1) - .map(|v| v.parse::()) - .ok_or(std::io::Error::other("no interval found in config file"))? - .map(Duration::from_millis) - .map_err(std::io::Error::other)?; - Ok(Params { addr, interval }) -} - -fn generate_temperature() -> f32 { - rand::rng().random_range(18.0..23.0) -} - -fn main() -> Result<(), Box> { - let params = read_parameters_from_file()?; - let rt = tokio::runtime::Builder::new_current_thread().enable_all().build()?; - rt.block_on(async move { - let socket = Arc::new(tokio::net::UdpSocket::bind(SocketAddr::new(IpAddr::from([127, 0, 0, 1]), 10003)).await?); - let mut interval = tokio::time::interval(params.interval); - loop { - interval.tick().await; - let new_temperature = generate_temperature(); - let data = new_temperature.to_le_bytes(); - socket.send_to(&data, ¶ms.addr).await?; - } - }) -} diff --git a/smart-house/house/src/builders.rs b/smart-house/house/src/builders.rs deleted file mode 100644 index 924a65b..0000000 --- a/smart-house/house/src/builders.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::{Device, House, Room}; -use std::collections::HashMap; - -pub struct HouseBuilder { - rooms: HashMap, -} - -impl HouseBuilder { - pub fn new() -> Self { - Self { rooms: HashMap::new() } - } - - pub fn add_room(self, name: &str) -> RoomBuilder { - RoomBuilder { - parent: self, - name: name.to_string(), - devices: HashMap::new(), - } - } - - pub fn build(self) -> House { - House::new(self.rooms) - } -} - -impl Default for HouseBuilder { - fn default() -> Self { - Self::new() - } -} - -pub struct RoomBuilder { - parent: HouseBuilder, - name: String, - devices: HashMap, -} - -impl RoomBuilder { - pub fn add_device(mut self, name: &str, device: impl Into) -> Self { - self.devices.insert(name.to_string(), device.into()); - self - } - - pub fn add_room(mut self, name: &str) -> RoomBuilder { - self.parent.rooms.insert(self.name, Room::new(self.devices)); - self.parent.add_room(name) - } - - pub fn build(mut self) -> House { - self.parent.rooms.insert(self.name, Room::new(self.devices)); - self.parent.build() - } -} diff --git a/smart-house/house/src/device.rs b/smart-house/house/src/device.rs deleted file mode 100644 index 88681ff..0000000 --- a/smart-house/house/src/device.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::PrintStatus; -use std::fmt::Display; -use std::io::Write; - -#[derive(Debug)] -pub enum Device { - Thermometer(super::Thermometer), - PowerSocket(super::PowerSocket), -} - -impl Device { - pub fn display(&self) -> impl Display { - match self { - Device::Thermometer(thermometer) => { - format!("DEV:{}", thermometer.display()) - } - Device::PowerSocket(power_socket) => { - format!("DEV:{}", power_socket.display()) - } - } - } -} - -impl From for Device { - fn from(value: super::Thermometer) -> Self { - Device::Thermometer(value) - } -} - -impl From for Device { - fn from(value: super::PowerSocket) -> Self { - Device::PowerSocket(value) - } -} - -impl PrintStatus for Device { - fn print_status_into(&self, out: &mut dyn Write) -> Result<(), std::io::Error> { - out.write_fmt(format_args!("{}", self.display())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{PowerSocket, Thermometer}; - - #[test] - fn smoke_test() { - let dev_thermometer = Device::Thermometer(Thermometer::stub(20.1)); - let dev_power_socket = Device::PowerSocket(PowerSocket::stub(11.2, false)); - - dev_thermometer.print_status(); - dev_power_socket.print_status(); - - let Device::Thermometer(thermometer) = dev_thermometer else { unreachable!() }; - let Device::PowerSocket(power_socket) = dev_power_socket else { - unreachable!() - }; - - assert_eq!(format!("{}", thermometer.display()), "Thermometer[ 20.1 ]"); - assert_eq!(format!("{}", power_socket.display()), "PowerSocket[ OFF : 0.0 ]"); - } - - #[test] - fn display_test() { - let dev_thermometer = Device::Thermometer(Thermometer::stub(20.1)); - let dev_power_socket = Device::PowerSocket(PowerSocket::stub(11.2, false)); - - assert_eq!(format!("{}", dev_thermometer.display()), "DEV:Thermometer[ 20.1 ]"); - assert_eq!(format!("{}", dev_power_socket.display()), "DEV:PowerSocket[ OFF : 0.0 ]"); - } -} diff --git a/smart-house/house/src/error.rs b/smart-house/house/src/error.rs deleted file mode 100644 index edb1227..0000000 --- a/smart-house/house/src/error.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::fmt::{Display, Formatter}; - -#[derive(Debug)] -pub struct Error { - message: String, -} - -impl Error { - pub fn new(message: impl AsRef) -> Self { - Self { - message: message.as_ref().to_string(), - } - } -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("{}", self.message))?; - Ok(()) - } -} - -impl std::error::Error for Error {} diff --git a/smart-house/house/src/house.rs b/smart-house/house/src/house.rs deleted file mode 100644 index 792e9ed..0000000 --- a/smart-house/house/src/house.rs +++ /dev/null @@ -1,154 +0,0 @@ -use crate::{Device, Error, PrintStatus, Room}; -use std::collections::HashMap; -use std::io::Write; - -#[derive(Debug)] -pub struct House { - rooms: HashMap, -} - -impl House { - pub fn new(rooms: HashMap) -> Self { - Self { rooms } - } - - pub fn get_room(&self, key: &str) -> Option<&Room> { - self.rooms.get(key) - } - - pub fn get_room_mut(&mut self, key: &str) -> Option<&mut Room> { - self.rooms.get_mut(key) - } - - pub fn insert_room(&mut self, name: &str, room: Room) -> Option { - self.rooms.insert(name.to_string(), room) - } - - pub fn remove_room(&mut self, key: &str) -> Option { - self.rooms.remove(key) - } - - pub fn get_device(&self, room_name: &str, device_name: &str) -> Result<&Device, Error> { - let Some(room) = self.get_room(room_name) else { - return Err(Error::new(format!("no room named '{}' found", room_name))); - }; - let Some(device) = room.get_device(device_name) else { - return Err(Error::new(format!("no device named '{}' found in room '{}'", device_name, room_name))); - }; - Ok(device) - } -} - -impl PrintStatus for House { - fn print_status_into(&self, out: &mut dyn Write) -> Result<(), std::io::Error> { - for (room_name, room) in self.rooms.iter() { - out.write_fmt(format_args!("{}:\n", room_name))?; - out.write_fmt(format_args!("{}\n", "-".repeat(32)))?; - room.print_status_into(out)?; - out.write_fmt(format_args!("\n"))?; - println!(); - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{Device, PowerSocket, Thermometer}; - - fn create_test_house() -> House { - House::new( - [ - ( - "main".to_string(), - Room::new( - [ - ("ThermA".to_string(), Thermometer::stub(20.0).into()), - ("PSocA".to_string(), PowerSocket::stub(12.34, false).into()), - ("PSocB".to_string(), PowerSocket::stub(10.01, true).into()), - ] - .into_iter() - .collect(), - ), - ), - ( - "bedroom".to_string(), - Room::new( - [ - ("PSocC".to_string(), PowerSocket::stub(11.11, true).into()), - ("ThermB".to_string(), Thermometer::stub(17.99).into()), - ] - .into_iter() - .collect(), - ), - ), - ] - .into_iter() - .collect(), - ) - } - - #[test] - fn smoke_test() { - let mut house = create_test_house(); - assert_eq!(house.rooms.len(), 2); - house.print_status(); - - assert_eq!( - format!("{}", house.get_room("main").unwrap().get_device("ThermA").unwrap().display()), - "DEV:Thermometer[ 20.0 ]" - ); - assert_eq!( - format!("{}", house.get_room("main").unwrap().get_device("PSocA").unwrap().display()), - "DEV:PowerSocket[ OFF : 0.0 ]" - ); - assert_eq!( - format!("{}", house.get_room("bedroom").unwrap().get_device("PSocC").unwrap().display()), - "DEV:PowerSocket[ ON : 11.1 ]" - ); - - let Device::PowerSocket(powers_socket) = house.get_room_mut("main").unwrap().get_device_mut("PSocA").unwrap() else { - unreachable!() - }; - powers_socket.set_on(true).unwrap(); - - assert_eq!( - format!("{}", house.get_room("main").unwrap().get_device("PSocA").unwrap().display()), - "DEV:PowerSocket[ ON : 12.3 ]" - ); - } - - #[test] - fn check_out_of_bounds() { - let house = create_test_house(); - assert!(house.get_room("absent").is_none()); - } - - #[test] - fn test_add_remove() { - let mut house = create_test_house(); - let room = Room::new(HashMap::new()); - - let result = house.insert_room("empty", room); - assert!(result.is_none()); - assert_eq!(house.rooms.len(), 3); - - let Some(result) = house.remove_room("bedroom") else { unreachable!() }; - assert_eq!(result.get_device("ThermB").unwrap().display().to_string(), "DEV:Thermometer[ 18.0 ]"); - } - - #[test] - fn test_get_device() { - let house = create_test_house(); - - let result = house.get_device("empty", "dummy"); - assert_eq!(result.unwrap_err().to_string(), "no room named 'empty' found"); - - let result = house.get_device("main", "dummy"); - assert_eq!(result.unwrap_err().to_string(), "no device named 'dummy' found in room 'main'"); - - let result = house.get_device("main", "ThermA"); - assert_eq!(result.unwrap().display().to_string(), "DEV:Thermometer[ 20.0 ]"); - } -} diff --git a/smart-house/house/src/lib.rs b/smart-house/house/src/lib.rs deleted file mode 100644 index e54f81b..0000000 --- a/smart-house/house/src/lib.rs +++ /dev/null @@ -1,22 +0,0 @@ -mod device; -mod error; -mod house; -mod power_socket; -#[macro_use] -mod room; -mod builders; -mod print_status; -mod reporter; -mod subscriber; -mod thermometer; - -pub use builders::{HouseBuilder, RoomBuilder}; -pub use device::Device; -pub use error::Error; -pub use house::House; -pub use power_socket::PowerSocket; -pub use print_status::PrintStatus; -pub use reporter::Reporter; -pub use room::Room; -pub use subscriber::Subscriber; -pub use thermometer::Thermometer; diff --git a/smart-house/house/src/main.rs b/smart-house/house/src/main.rs deleted file mode 100644 index f328e4d..0000000 --- a/smart-house/house/src/main.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() {} diff --git a/smart-house/house/src/power_socket.rs b/smart-house/house/src/power_socket.rs deleted file mode 100644 index 4de6436..0000000 --- a/smart-house/house/src/power_socket.rs +++ /dev/null @@ -1,177 +0,0 @@ -use std::cell::RefCell; -use std::fmt::{Debug, Display}; -use std::io::{Read, Write}; -use std::net::{SocketAddr, TcpStream}; -use std::time::Duration; - -#[derive(Debug)] -pub struct PowerSocket { - handle: Box, -} - -impl PowerSocket { - pub fn stub(power_rate: f32, on: bool) -> Self { - Self { - handle: Box::new(PowerSocketStub::new(power_rate, on)), - } - } - - pub fn connect(addr: &str) -> Result { - let handle = PowerSocketClient::new(addr)?; - Ok(Self { handle: Box::new(handle) }) - } - - pub fn is_on(&self) -> Result { - self.handle.is_on() - } - - pub fn set_on(&mut self, on: bool) -> Result<(), std::io::Error> { - self.handle.set_on(on) - } - - pub fn get_power(&self) -> Result { - self.handle.get_power() - } - - pub fn display(&self) -> impl Display { - const ERR: &str = "PowerSocket[ ERR ]"; - let power = match self.get_power() { - Ok(power) => power, - Err(e) => { - eprintln!("error on get power: {:?}", e); - return ERR.to_string(); - } - }; - let on = match self.is_on() { - Ok(state) => state, - Err(e) => { - eprintln!("error on get state: {:?}", e); - return ERR.to_string(); - } - }; - let state = if on { "ON" } else { "OFF" }; - format!("PowerSocket[ {} : {:02.1} ]", state, power) - } -} - -trait PowerSocketHandle: Debug { - fn is_on(&self) -> Result; - - fn set_on(&mut self, on: bool) -> Result<(), std::io::Error>; - - fn get_power(&self) -> Result; -} - -#[derive(Debug)] -struct PowerSocketStub { - power_rate: f32, - on: bool, -} - -impl PowerSocketStub { - pub fn new(power_rate: f32, on: bool) -> Self { - Self { power_rate, on } - } -} - -impl PowerSocketHandle for PowerSocketStub { - fn is_on(&self) -> Result { - Ok(self.on) - } - - fn set_on(&mut self, on: bool) -> Result<(), std::io::Error> { - self.on = on; - Ok(()) - } - - fn get_power(&self) -> Result { - Ok(if self.on { self.power_rate } else { 0.0 }) - } -} - -const TIMEOUT: Duration = Duration::from_secs(5); - -#[derive(Debug)] -struct PowerSocketClient { - stream: RefCell, -} - -impl PowerSocketClient { - fn new(addr: &str) -> Result { - let addr: SocketAddr = addr.parse().map_err(std::io::Error::other)?; - let stream = TcpStream::connect_timeout(&addr, TIMEOUT)?; - stream.set_write_timeout(Some(TIMEOUT))?; - stream.set_read_timeout(Some(TIMEOUT))?; - Ok(Self { stream: RefCell::new(stream) }) - } -} - -const CMD_GET_ON: u8 = 1; -const CMD_TURN_ON: u8 = 2; -const CMD_TURN_OFF: u8 = 3; -const CMD_GET_POWER: u8 = 4; - -impl PowerSocketHandle for PowerSocketClient { - fn is_on(&self) -> Result { - let mut buf = [CMD_GET_ON; 1]; - self.stream - .borrow_mut() - .write_all(&buf) - .map_err(|e| std::io::Error::other(format!("CMD_GET_ON request error: {:?}", e)))?; - self.stream - .borrow_mut() - .read_exact(&mut buf) - .map_err(|e| std::io::Error::other(format!("CMD_GET_ON response error: {:?}", e)))?; - Ok(!matches!(buf, [0])) - } - - fn set_on(&mut self, on: bool) -> Result<(), std::io::Error> { - let cmd = if on { CMD_TURN_ON } else { CMD_TURN_OFF }; - let mut buf = [cmd; 1]; - self.stream - .borrow_mut() - .write_all(&buf) - .map_err(|e| std::io::Error::other(format!("change state request error: {:?}", e)))?; - self.stream - .borrow_mut() - .read_exact(&mut buf) - .map_err(|e| std::io::Error::other(format!("change state response error: {:?}", e)))?; - Ok(()) - } - - fn get_power(&self) -> Result { - let mut buf = [CMD_GET_POWER; 4]; - self.stream - .borrow_mut() - .write_all(&buf[0..1]) - .map_err(|e| std::io::Error::other(format!("CMD_GET_POWER request error: {:?}", e)))?; - self.stream - .borrow_mut() - .read_exact(&mut buf) - .map_err(|e| std::io::Error::other(format!("CMD_GET_POWER response error: {:?}", e)))?; - Ok(f32::from_le_bytes(buf)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn smoke_test() { - let mut power_socket = PowerSocket::stub(12.4, false); - assert!(!power_socket.is_on().unwrap()); - assert_eq!(power_socket.get_power().unwrap(), 0.0); - - power_socket.set_on(true).unwrap(); - assert!(power_socket.is_on().unwrap()); - assert_eq!(power_socket.get_power().unwrap(), 12.4); - } - - #[test] - fn display_test() { - assert_eq!(format!("{}", PowerSocket::stub(11.549, false).display()), "PowerSocket[ OFF : 0.0 ]"); - assert_eq!(format!("{}", PowerSocket::stub(11.549, true).display()), "PowerSocket[ ON : 11.5 ]"); - assert_eq!(format!("{}", PowerSocket::stub(11.550, true).display()), "PowerSocket[ ON : 11.6 ]"); - } -} diff --git a/smart-house/house/src/print_status.rs b/smart-house/house/src/print_status.rs deleted file mode 100644 index 9a0c4f3..0000000 --- a/smart-house/house/src/print_status.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub trait PrintStatus { - fn print_status_into(&self, out: &mut dyn std::io::Write) -> Result<(), std::io::Error>; - - fn print_status(&self) { - if let Err(e) = self.print_status_into(&mut std::io::stdout()) { - eprintln!("Unexpected print error: {:?}", e); - } - } -} diff --git a/smart-house/house/src/reporter.rs b/smart-house/house/src/reporter.rs deleted file mode 100644 index 42541fc..0000000 --- a/smart-house/house/src/reporter.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::PrintStatus; - -pub struct Reporter<'a> { - reportables: Vec<&'a dyn PrintStatus>, -} - -impl<'a> Reporter<'a> { - pub fn new() -> Self { - Self { reportables: Vec::new() } - } - - pub fn add_reportable(mut self, reportable: &'a T) -> Self { - self.reportables.push(reportable); - self - } - - pub fn report(&self) { - println!("{}", "=".repeat(16)); - for reportable in &self.reportables { - reportable.print_status(); - println!("\n{}", "=".repeat(16)); - } - } -} - -impl<'a> Default for Reporter<'a> { - fn default() -> Self { - Self::new() - } -} diff --git a/smart-house/house/src/room.rs b/smart-house/house/src/room.rs deleted file mode 100644 index 0437521..0000000 --- a/smart-house/house/src/room.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::{Device, PrintStatus, Subscriber}; -use std::collections::HashMap; -use std::fmt::{Debug, Formatter}; -use std::io::Write; - -pub struct Room { - devices: HashMap, - subscribers: Vec>, -} - -impl Room { - pub fn new(devices: HashMap) -> Self { - Self { - devices, - subscribers: Vec::new(), - } - } - - pub fn get_device(&self, name: &str) -> Option<&Device> { - self.devices.get(name) - } - - pub fn get_device_mut(&mut self, name: &str) -> Option<&mut Device> { - self.devices.get_mut(name) - } - - pub fn insert_device(&mut self, name: &str, device: Device) -> Option { - for subscriber in &mut self.subscribers { - subscriber.on_event(&device); - } - self.devices.insert(name.to_string(), device) - } - - pub fn remove_device(&mut self, name: &str) -> Option { - self.devices.remove(name) - } - - pub fn subscribe(&mut self, subscriber: impl Subscriber + 'static) { - self.subscribers.push(Box::new(subscriber)); - } -} - -impl PrintStatus for Room { - fn print_status_into(&self, out: &mut dyn Write) -> Result<(), std::io::Error> { - for (name, device) in self.devices.iter() { - out.write_fmt(format_args!("{} => ", name))?; - device.print_status_into(out)?; - } - Ok(()) - } -} - -impl Debug for Room { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Room") - .field("devices", &self.devices) - .field("subscribers.len()", &self.subscribers.len()) - .finish() - } -} - -impl Default for Room { - fn default() -> Self { - Room::new(HashMap::new()) - } -} - -#[macro_export] -macro_rules! room { - ($($key:expr => $dev:expr),* $(,)?) => { - Room::new([$( - ($key.to_string(), $dev.into()), - )*].into_iter().collect()) - }; -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{PowerSocket, Thermometer}; - - fn create_test_room() -> Room { - room!( - "PSoc" => PowerSocket::stub(12.34, false), - "Therm" => Thermometer::stub(21.56), - ) - } - - #[test] - fn smoke_test() { - let mut room = create_test_room(); - assert_eq!(room.devices.len(), 2); - room.print_status(); - - assert_eq!(format!("{}", room.get_device("PSoc").unwrap().display()), "DEV:PowerSocket[ OFF : 0.0 ]"); - assert_eq!(format!("{}", room.get_device("Therm").unwrap().display()), "DEV:Thermometer[ 21.6 ]"); - - let Device::PowerSocket(power_socket) = room.get_device_mut("PSoc").unwrap() else { - unreachable!() - }; - power_socket.set_on(true).unwrap(); - - assert_eq!(format!("{}", room.get_device("PSoc").unwrap().display()), "DEV:PowerSocket[ ON : 12.3 ]"); - assert_eq!(format!("{}", room.get_device("Therm").unwrap().display()), "DEV:Thermometer[ 21.6 ]"); - } - - #[test] - fn check_out_of_bounds() { - let room = create_test_room(); - assert!(room.get_device("dummy").is_none()); - } - - #[test] - fn test_add_remove() { - let mut room = create_test_room(); - let result = room.insert_device("NewTerm", Thermometer::stub(20.0).into()); - assert!(result.is_none()); - assert_eq!(room.devices.len(), 3); - - let Some(Device::Thermometer(removed)) = room.remove_device("Therm") else { - unreachable!() - }; - assert_eq!(removed.get_temperature().unwrap(), 21.56); - assert_eq!(room.devices.len(), 2); - } -} diff --git a/smart-house/house/src/subscriber.rs b/smart-house/house/src/subscriber.rs deleted file mode 100644 index 42194ed..0000000 --- a/smart-house/house/src/subscriber.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::Device; - -pub trait Subscriber { - fn on_event(&mut self, device: &Device); -} - -impl Subscriber for F -where - F: Fn(&Device), -{ - fn on_event(&mut self, value: &Device) { - self(value) - } -} diff --git a/smart-house/house/src/thermometer.rs b/smart-house/house/src/thermometer.rs deleted file mode 100644 index b2097fa..0000000 --- a/smart-house/house/src/thermometer.rs +++ /dev/null @@ -1,121 +0,0 @@ -use std::fmt::{Debug, Display}; -use std::net::UdpSocket; -use std::sync::Arc; -use std::sync::atomic::{AtomicU32, Ordering}; -use std::time::Duration; - -#[derive(Debug)] -pub struct Thermometer { - handle: Box, -} - -impl Thermometer { - pub fn stub(temperature: f32) -> Self { - Self { - handle: Box::new(ThermometerHandleStub::new(temperature)), - } - } - - pub fn connect(addr: &str) -> std::io::Result { - let handle = ThermometerClient::new(addr)?; - Ok(Self { handle: Box::new(handle) }) - } - - pub fn get_temperature(&self) -> Result { - self.handle.get_temperature() - } - - pub fn display(&self) -> impl Display { - let output = self.get_temperature(); - match output { - Ok(v) => format!("Thermometer[ {:02.1} ]", v), - Err(e) => { - eprintln!("error fetching temperature: {:?}", e); - "Thermometer[ ERR ]".to_string() - } - } - } -} - -trait ThermometerHandle: Debug { - fn get_temperature(&self) -> Result; -} - -#[derive(Debug)] -struct ThermometerHandleStub { - temperature: f32, -} - -impl ThermometerHandleStub { - fn new(temperature: f32) -> Self { - Self { temperature } - } -} - -impl ThermometerHandle for ThermometerHandleStub { - fn get_temperature(&self) -> Result { - Ok(self.temperature) - } -} - -const TIMEOUT: Duration = Duration::from_secs(5); - -#[derive(Debug)] -struct ThermometerClient { - value: Arc, -} - -impl ThermometerClient { - fn read(socket: &UdpSocket) -> std::io::Result { - let mut buf = [0u8; 4]; - let result = socket.recv_from(&mut buf); - result.map(|_| Ok(u32::from_le_bytes(buf)))? - } - - fn new(addr: &str) -> std::io::Result { - let value = Arc::new(AtomicU32::new(u32::from_le_bytes(f32::NAN.to_le_bytes()))); - let socket = UdpSocket::bind(addr)?; - socket.set_read_timeout(Some(TIMEOUT))?; - - let data = ThermometerClient::read(&socket)?; - value.store(data, Ordering::Relaxed); - - let weak_value = Arc::downgrade(&value); - std::thread::spawn(move || { - while let Some(value) = weak_value.upgrade() { - let result = ThermometerClient::read(&socket); - match result { - Ok(data) => value.store(data, Ordering::Relaxed), - Err(e) => { - eprintln!("receiving data failed: {:?}", e); - break; - } - }; - } - }); - Ok(Self { value }) - } -} - -impl ThermometerHandle for ThermometerClient { - fn get_temperature(&self) -> Result { - Ok(f32::from_le_bytes(self.value.load(Ordering::Relaxed).to_le_bytes())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn smoke_test() { - let thermometer = Thermometer::stub(20.0); - assert_eq!(thermometer.get_temperature().unwrap(), 20.0); - } - - #[test] - fn display_test() { - assert_eq!(format!("{}", Thermometer::stub(19.550).display()), "Thermometer[ 19.5 ]"); - assert_eq!(format!("{}", Thermometer::stub(19.551).display()), "Thermometer[ 19.6 ]"); - } -} diff --git a/smart-house/power_socket_lib/Cargo.toml b/smart-house/power_socket_lib/Cargo.toml new file mode 100644 index 0000000..b634800 --- /dev/null +++ b/smart-house/power_socket_lib/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "power_socket_lib" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["rlib", "staticlib", "cdylib"] + +[dependencies] diff --git a/smart-house/power_socket_lib/src/lib.rs b/smart-house/power_socket_lib/src/lib.rs new file mode 100644 index 0000000..e6514ef --- /dev/null +++ b/smart-house/power_socket_lib/src/lib.rs @@ -0,0 +1,45 @@ +#[repr(C)] +pub struct PowerSocket { + power_rate: f32, + on: bool, +} + +#[unsafe(no_mangle)] +pub extern "C" fn power_socket_new(power_rate: f32, on: bool) -> PowerSocket { + PowerSocket { power_rate, on } +} + +#[unsafe(no_mangle)] +pub extern "C" fn power_socket_is_on(power_socket: &PowerSocket) -> bool { + power_socket.on +} + +#[unsafe(no_mangle)] +pub extern "C" fn power_socket_set_on(power_socket: &mut PowerSocket, on: bool) { + power_socket.on = on +} + +#[unsafe(no_mangle)] +pub extern "C" fn power_socket_get_power(power_socket: &PowerSocket) -> f32 { + if power_socket.on { power_socket.power_rate } else { 0.0 } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let mut power_socket = power_socket_new(12.0, false); + assert_eq!(power_socket.power_rate, 12.0); + assert_eq!(power_socket.on, false); + + assert_eq!(power_socket_is_on(&power_socket), false); + assert_eq!(power_socket_get_power(&power_socket), 0.0); + + power_socket_set_on(&mut power_socket, true); + + assert_eq!(power_socket_is_on(&power_socket), true); + assert_eq!(power_socket_get_power(&power_socket), 12.0); + } +} diff --git a/smart-house/thermometer_mock.cfg b/smart-house/thermometer_mock.cfg deleted file mode 100644 index 62f58b9..0000000 --- a/smart-house/thermometer_mock.cfg +++ /dev/null @@ -1,2 +0,0 @@ -127.0.0.1:10002 -500 diff --git a/smart-house/use_dynamic/Cargo.toml b/smart-house/use_dynamic/Cargo.toml new file mode 100644 index 0000000..5c15007 --- /dev/null +++ b/smart-house/use_dynamic/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "use_dynamic" +version = "0.1.0" +edition = "2024" + +[dependencies] +libloading = "0.9.0" diff --git a/smart-house/use_dynamic/src/main.rs b/smart-house/use_dynamic/src/main.rs new file mode 100644 index 0000000..33bc270 --- /dev/null +++ b/smart-house/use_dynamic/src/main.rs @@ -0,0 +1,51 @@ +mod ps { + #[repr(C)] + #[derive(Debug)] + pub struct PowerSocket { + power_rate: f32, + on: bool, + } + + pub type FnPowerSocketNew = unsafe extern "C" fn(power_rate: f32, on: bool) -> PowerSocket; + pub type FnPowerSocketIsOn = unsafe extern "C" fn(power_socket: &PowerSocket) -> bool; + pub type FnPowerSocketSetOn = unsafe extern "C" fn(power_socket: &mut PowerSocket, on: bool); + pub type FnPowerSocketGetPower = unsafe extern "C" fn(power_socket: &PowerSocket) -> f32; +} + +use ps::*; + +fn main() { + let path = "target/debug/power_socket_lib.dll"; + let Ok(lib) = (unsafe { libloading::Library::new(path) }) else { + eprintln!("Failed to load lib: {}", path); + return; + }; + let Ok(power_socket_new) = (unsafe { lib.get::("power_socket_new") }) else { + eprintln!("Failed to get power_socket_new function from lib: {}", path); + return; + }; + let Ok(power_socket_is_on) = (unsafe { lib.get::("power_socket_is_on") }) else { + eprintln!("Failed to get power_socket_is_on function from lib: {}", path); + return; + }; + let Ok(power_socket_set_on) = (unsafe { lib.get::("power_socket_set_on") }) else { + eprintln!("Failed to get power_socket_set_on function from lib: {}", path); + return; + }; + let Ok(power_socket_get_power) = (unsafe { lib.get::("power_socket_get_power") }) else { + eprintln!("Failed to get power_socket_get_power function from lib: {}", path); + return; + }; + + let mut power_socket = unsafe { power_socket_new(12.0, false) }; + println!("call power_socket_new -> {:?}", power_socket); + + println!("call power_socket_is_on -> {:?}", unsafe { power_socket_is_on(&power_socket) }); + println!("call power_socket_get_power -> {:?}", unsafe { power_socket_get_power(&power_socket) }); + + unsafe { power_socket_set_on(&mut power_socket, true) }; + println!("call power_socket_set_on(&ref, true)"); + + println!("call power_socket_is_on -> {:?}", unsafe { power_socket_is_on(&power_socket) }); + println!("call power_socket_get_power -> {:?}", unsafe { power_socket_get_power(&power_socket) }); +} diff --git a/smart-house/use_static/Cargo.toml b/smart-house/use_static/Cargo.toml new file mode 100644 index 0000000..4f428ff --- /dev/null +++ b/smart-house/use_static/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "use_static" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/smart-house/use_static/build.rs b/smart-house/use_static/build.rs new file mode 100644 index 0000000..7ed55ff --- /dev/null +++ b/smart-house/use_static/build.rs @@ -0,0 +1,4 @@ +fn main() { + println!("cargo:rustc-link-lib=static=power_socket_lib"); + println!("cargo:rustc-link-search=target/debug"); +} diff --git a/smart-house/use_static/src/main.rs b/smart-house/use_static/src/main.rs new file mode 100644 index 0000000..7c9c726 --- /dev/null +++ b/smart-house/use_static/src/main.rs @@ -0,0 +1,34 @@ +mod ps { + #[repr(C)] + #[derive(Debug)] + pub struct PowerSocket { + power_rate: f32, + on: bool, + } + // #[link(name = "power_socket_lib", kind = "static")] + unsafe extern "C" { + pub fn power_socket_new(power_rate: f32, on: bool) -> PowerSocket; + + pub fn power_socket_is_on(power_socket: &PowerSocket) -> bool; + + pub fn power_socket_set_on(power_socket: &mut PowerSocket, on: bool); + + pub fn power_socket_get_power(power_socket: &PowerSocket) -> f32; + } +} + +use ps::*; + +fn main() { + let mut power_socket = unsafe { power_socket_new(12.0, false) }; + println!("call power_socket_new -> {:?}", power_socket); + + println!("call power_socket_is_on -> {:?}", unsafe { power_socket_is_on(&power_socket) }); + println!("call power_socket_get_power -> {:?}", unsafe { power_socket_get_power(&power_socket) }); + + unsafe { power_socket_set_on(&mut power_socket, true) }; + println!("call power_socket_set_on(&ref, true)"); + + println!("call power_socket_is_on -> {:?}", unsafe { power_socket_is_on(&power_socket) }); + println!("call power_socket_get_power -> {:?}", unsafe { power_socket_get_power(&power_socket) }); +}